0
votes

I am new to Laravel. I modified start up app from github code, to save the given post details into the database. But facing below 2 issues

  1. First value that entered before comma only inserting into the database. I think because of that in relation table inserting only one record.
  2. In edit post, categories showing as json structure

I had setup a database for many to many relation ship.

posts, categories and categories_post

Created a form to add/edit post

<form class="form-horizontal" method="post" action="@if (isset($post)){{ URL::to('admin/blogs/' . $post->id . '/edit') }}@endif" autocomplete="off">
        <!-- CSRF Token -->
        <input type="hidden" name="_token" value="{{{ csrf_token() }}}" />
        <!-- ./ csrf token -->

        <!-- Tabs Content -->
        <div class="tab-content">
            <!-- General tab -->
            <div class="tab-pane active" id="tab-general">
                <!-- Post Title -->
                <div class="form-group {{{ $errors->has('title') ? 'error' : '' }}}">
                    <div class="col-md-12">
                        <label class="control-label" for="title">Post Title</label>
                        <input class="form-control" type="text" name="title" id="title" value="{{{ Input::old('title', isset($post) ? $post->title : null) }}}" />
                        {{{ $errors->first('title', '<span class="help-block">:message</span>') }}}
                    </div>
                </div>
                <div class="form-group {{{ $errors->has('categories') ? 'error' : '' }}}">
                    <div class="col-md-12">
                        <label class="control-label" for="categories">Categories</label>
                        <input class="form-control" type="text" name="categories" id="categories" value="{{{ Input::old('categories', isset($post) ? $post->categories : null) }}}" />
                        {{{ $errors->first('categories', '<span class="help-block">:message</span>') }}}
                    </div>
                </div>
                <!-- ./ post title -->

                <!-- Content -->
                <div class="form-group {{{ $errors->has('content') ? 'has-error' : '' }}}">
                    <div class="col-md-12">
                        <label class="control-label" for="content">Content</label>
                        <textarea class="ckeditor" name="content" value="content" rows="10">{{{ Input::old('content', isset($post) ? $post->content : null) }}}</textarea>
                        {{{ $errors->first('content', '<span class="help-block">:message</span>') }}}
                    </div>
                </div>
                <!-- ./ content -->
            </div>
            <!-- ./ general tab -->
        </div>
        <!-- ./ tabs content -->

        <!-- Form Actions -->
        <div class="form-group">
            <div class="col-md-12">
                <element class="btn-cancel close_popup">Cancel</element>
                <button type="reset" class="btn btn-default">Reset</button>
                <button type="submit" class="btn btn-success">Update</button>
            </div>
        </div>
        <!-- ./ form actions -->
    </form>

Here is the controller code

// Start transaction!
        DB::beginTransaction();

        $title = Input::get('title');
        $categoriesString = Input::get('categories');
        $categories = explode(',', $categoriesString);
        $postDetails = $this->post->findByTitle($title);
        if($postDetails)
        {
            return Redirect::to('blog/create')->withInput()->withErrors(array('title' => 'Title already exists'));
        } else {
            // Update the blog post data
            $this->post->title            = $title;
            $this->post->slug             = Str::slug(Input::get('title'));
            $this->post->content          = Input::get('content');
        }

        try 
        { // Validate, then create if valid
            $newPost = $this->post->save();
        } catch(ValidationException $e)
        {
            // Rollback and then redirect
            // back to form with errors
            DB::rollback();
            return Redirect::to('admin/blogs/create')->with('error', Lang::get('admin/blogs/messages.create.error'));
        } catch(\Exception $e)
        {
            DB::rollback();
            throw $e;
        }

Taking the categories as comma separated from form

        $categoriesString = Input::get('categories');
        $categories = explode(',', $categoriesString);

Then checking the category already exists in the database. If exists taking the id of it and appending to new array.

        $categoryClass = new Categories();
        $categoryIds = array();
        foreach($categories as $category)
        {
            try {
                // Validate, then create if valid
                $categoryName = strtolower($category);
                $categoryDetails = $categoryClass->findByName($categoryName);
                if($categoryDetails)
                {
                    $categoryId = $categoryDetails->id;
                } else {    
                    $categoryClass->name = $category;
                    $categoryClass->save();
                    $categoryId = $categoryClass->id;
                } 
            } catch(ValidationException $e)
            {
                // Rollback and then redirect
                // back to form with errors
                DB::rollback();
                return Redirect::to('blog/create')->with('error', Lang::get('blog/messages.create.error'));
            } catch(\Exception $e)
            {
                DB::rollback();
                throw $e;
            }

            // Push category id into category id's array
            $categoryIds[] = $categoryId;
        }

        //Then sync the categories with new categories  
        $this->post->categories()->sync($categoryIds);

        // Commit
        DB::commit();

        return Redirect::to('blog/' . $this->post->id . '/edit')->with('success', Lang::get('blog/messages.create.success'));

POST Model

    /**
     * Get the post's categories.
     *
     * @return array
     */
    public function categories()
    {
        return $this->belongsToMany('Categories');
    }

Categories Model

    /**
     * Get the Categories's posts.
     *
     * @return array
     */
    public function posts()
    {
        return $this->belongsToMany('Post');
    }

Someone please help me to get out of this issues.

1

1 Answers

3
votes

There is a much easier day of doing this. This how I store my articles, with tags that can be entered comma separated.

The article controller store function: First create the article object, set the user_id to the current user session id and fill all fields from the form. If the article gets saved attach tags with a static method in the tag model.

public function store()
{
    $article = new Article();
    $article->user_id = Auth::id();
    $article->fill(Input::all());

    if ($article->save())
    {
        Tag::attach($article, Input::get('tags'));

        return Redirect::route('articles.index')
            ->with(['flashMessage' => 'Article created.', 'flashType' => 'success']);
    } else {
        return Redirect::back()->withErrors($article->errors())->withInput();
    }
}

The article model, set fillable property and tag relations:

protected $fillable = ['category_id', 'title', 'description', 'content', 'published'];

public function tags()
{
    return $this->belongsToMany('Tag', 'article_tag', 'article_id', 'tag_id');
}

The tag model article relation:

public function articles()
{
    return $this->belongsToMany('Article', 'article_tag', 'tag_id', 'article_id');
}

The tag model attach shope: First create empty array to hold tags, explode the comma separated tags and enumerate though them. Trim the exploded tags to eliminate white spaces. Get the TagID from the tag table, if empty (meaning tag does not exist) create it. Add each TagID to the tags array and finally sync the article model with the tags array.

public function scopeAttach($query, $model, $tags)
{
    $returnTags = array();

    foreach (explode(',', $tags) as $tag)
    {
        $tag = trim(Str::slug($tag));

        if ($tag != '')
        {
            $tagID = Tag::where('name', '=', $tag)->pluck('id');

            if (empty($tagID))
            {
                $tagNew = new Tag;
                $tagNew->name = $tag;
                $tagNew->save();

                $tagID = $tagNew->id;
            }

            $returnTags[] = $tagID;
        }
    }

    $model->tags()->sync($returnTags);
}

View presenter implode tags function: Enumerate through all tags and implode to comma separated string.

public function implodeTags()
{
    $returnString = null;

    foreach ($this->tags as $tag)
    {
        if (!empty($returnString)) $returnString .= ", ";
        $returnString .= $tag->name;
    }

    return $returnString;
}

The create/edit blade view: First open new form for create or bind with model for edit. If create; show input field with no value. If edit; use implodeTags function from view presenter to fill text field with a comma separated list of tags.

@if (!isset($edit))
  @section('title', 'Create article')
  {{ Form::open(array('route' => 'articles.store', 'role' => 'form')) }}
@else
  @section('title', 'Edit article')
  {{ Form::model($article, array('method' => 'PUT', 'route' => ['articles.update', $article->id], 'role' => 'form')) }}
@endif

...

<div class="form-group @if ($errors->has('tags')) has-error @endif">
    {{ Form::label('tags', 'Tags') }}
    @if(!isset($article))
        {{ Form::text('tags', null, array('class' => 'form-control')) }}
    @else
        {{ Form::text('tags', $article->present()->implodeTags, array('class' => 'form-control')) }}
    @endif
    @if ($errors->has('tags')) <p class="help-block">{{ $errors->first('tags') }}</p> @endif
</div>

...

Obviously the create/edit view contains more than this, but these are the parts relevant for the tags implementation.