Laravel E-Commerce Application Development – Attributes Section Part 2

Laravel E-Commerce Application Development – Attributes Section Part 2

Laravel E-Commerce Application Development ( 14 Lessons )

In this course, you’ll learn how to create an E-Commerce Website from scratch in Laravel. The process has never been easier I’ll take you from the very beginning stages of setting up Laravel till the last steps of adding products to the cart. If you’ve good understanding & experience in PHP & MySQL then this course is for you.

see full series
  1. Laravel E-Commerce Application Development – Introduction
  2. Laravel E-Commerce Application Development – Initial Project Setup
  3. Laravel E-Commerce Application Development – Assets Setup Using Laravel Mix
  4. Laravel E-Commerce Application Development – Admin Model and Migration
  5. Laravel E-Commerce Application Development – Backend Admin Authentication
  6. Laravel E-Commerce Application Development – Base Controller and Repository
  7. Laravel E-Commerce Application Development – Settings Section Part 1
  8. Laravel E-Commerce Application Development – Settings Section Part 2
  9. Laravel E-Commerce Application Development – Categories Section Part 1
  10. Laravel E-Commerce Application Development – Categories Section Part 2
  11. Laravel E-Commerce Application Development – Attributes Section Part 1
  12. Laravel E-Commerce Application Development – Attributes Section Part 2
  13. Laravel E-Commerce Application Development – Attributes Section Part 3
  14. Laravel E-Commerce Application Development – Brands Section

Spread the word! Share with your fellow developers.

This is part 11 of the Laravel E-Commerce Application Development series. In this part will add the admin section for our attributes CRUD.

So let’s start implementing this in our application.

Adding Menu Link for Attributes

First thing first, we need to add the attributes link to our menu in admin section. For that open the sidebar.blade.php file from resources/views/admin/partials folder and add the below code snippets just right after the html block for our categories link.

<li>
    <a class="app-menu__item {{ Route::currentRouteName() == 'admin.attributes.index' ? 'active' : '' }}" href="{{ route('admin.attributes.index') }}">
        <i class="app-menu__icon fa fa-th"></i>
        <span class="app-menu__label">Attributes</span>
    </a>
</li>

Creating Attribute Contract

In this section, we will create an interface for our Attribute model. Create a new file called AttributeContract.php in app/Contracts folder and add the below code block in it.

namespace App\Contracts;

interface AttributeContract
{
    /**
     * @param string $order
     * @param string $sort
     * @param array $columns
     * @return mixed
     */
    public function listAttributes(string $order = 'id', string $sort = 'desc', array $columns = ['*']);

    /**
     * @param int $id
     * @return mixed
     */
    public function findAttributeById(int $id);

    /**
     * @param array $params
     * @return mixed
     */
    public function createAttribute(array $params);

    /**
     * @param array $params
     * @return mixed
     */
    public function updateAttribute(array $params);

    /**
     * @param $id
     * @return bool
     */
    public function deleteAttribute($id);
}

In this contract, we have added some method signatures to deal with our Attribute model. Just like we did in the categories section.

Creating Attribute Repository

Next, create a new file in the app/Repositories folder and name it AttributeRepository. Add the below code in it so we will have a repository which will implement our AttributeContract interface and extend the base repository.

namespace App\Repositories;

use App\Models\Attribute;
use App\Contracts\AttributeContract;
use Illuminate\Database\QueryException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Doctrine\Instantiator\Exception\InvalidArgumentException;

class AttributeRepository extends BaseRepository implements AttributeContract
{
    /**
     * AttributeRepository constructor.
     * @param Attribute $model
     */
    public function __construct(Attribute $model)
    {
        parent::__construct($model);
        $this->model = $model;
    }

    /**
     * @param string $order
     * @param string $sort
     * @param array $columns
     * @return mixed
     */
    public function listAttributes(string $order = 'id', string $sort = 'desc', array $columns = ['*'])
    {
        return $this->all($columns, $order, $sort);
    }

    /**
     * @param int $id
     * @return mixed
     * @throws ModelNotFoundException
     */
    public function findAttributeById(int $id)
    {
        try {
            return $this->findOneOrFail($id);

        } catch (ModelNotFoundException $e) {

            throw new ModelNotFoundException($e);
        }

    }

    /**
     * @param array $params
     * @return Category|mixed
     */
    public function createAttribute(array $params)
    {
        try {
            $collection = collect($params);

            $is_filterable = $collection->has('is_filterable') ? 1 : 0;
            $is_required = $collection->has('is_required') ? 1 : 0;

            $merge = $collection->merge(compact('is_filterable', 'is_required'));

            $attribute = new Attribute($merge->all());

            $attribute->save();

            return $attribute;

        } catch (QueryException $exception) {
            throw new InvalidArgumentException($exception->getMessage());
        }
    }

    /**
     * @param array $params
     * @return mixed
     */
    public function updateAttribute(array $params)
    {
        $attribute = $this->findAttributeById($params['id']);

        $collection = collect($params)->except('_token');

        $is_filterable = $collection->has('is_filterable') ? 1 : 0;
        $is_required = $collection->has('is_required') ? 1 : 0;

        $merge = $collection->merge(compact('is_filterable', 'is_required'));

        $attribute->update($merge->all());

        return $attribute;
    }

    /**
     * @param $id
     * @return bool|mixed
     */
    public function deleteAttribute($id)
    {
        $attribute = $this->findAttributeById($id);

        $attribute->delete();

        return $attribute;
    }
}

In the above code block, we added the implementation of our AttributeContract signatures. Again, the whole functionality is similar to what we did for our categories, nothing fancy.

Binding Attribute Contract to Repository

Next, we need to bind the AttributeContract to our AttributeRepository. Open the RepositoryServiceProvider class from app/Providers folder and include the interface and repository like below.

use App\Contracts\AttributeContract;
use App\Repositories\AttributeRepository;

Next, update the $repositories property like below.

protected $repositories = [
    CategoryContract::class         =>          CategoryRepository::class,
    AttributeContract::class        =>          AttributeRepository::class,
];

Now, we can use the AttributeContract interface in our controllers which will provide the access to our repository methods.

Learn More

If you want to learn more about repositories and how we can use them in Laravel, read our post on How to Use Repository Pattern in Laravel.

Adding Attribute Routes

Next, we will add the required routes for our admin’s attribute section. For this, open the admin.php file from routes folder and add the below routes in this file.

Route::group(['prefix'  =>   'attributes'], function() {

    Route::get('/', 'Admin\[email protected]')->name('admin.attributes.index');
    Route::get('/create', 'Admin\[email protected]')->name('admin.attributes.create');
    Route::post('/store', 'Admin\[email protected]')->name('admin.attributes.store');
    Route::get('/{id}/edit', 'Admin\[email protected]')->name('admin.attributes.edit');
    Route::post('/update', 'Admin\[email protected]')->name('admin.attributes.update');
    Route::get('/{id}/delete', 'Admin\[email protected]')->name('admin.attributes.delete');

});

Creating Attribute Controller

Now we will add the AttributeController as you might have noticed all our routes in above section are pointing to AttributeController. Open your command line terminal and run the below artisan command to generate a controller.

php artisan make:controller Admin\AttributeController

This will generate a simple controller file for you in app/Http/Controllers/Admin folder. Just like Category controller, we will use our BaseController to extend this Attribute controller.

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Contracts\AttributeContract;

class AttributeController extends BaseController
{
    //
}

Firstly, we will add the $attributeRepository property and inject our AttributeContract in it.

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Contracts\AttributeContract;

class AttributeController extends BaseController
{
    protected $attributeRepository;

    public function __construct(AttributeContract $attributeRepository)
    {
        $this->attributeRepository = $attributeRepository;
    }

}

Now, we need to add the index(), create(), store(), edit(), update() and delete() method in it which we will do in the coming sections.

Creating Attribures List Page

First thing, we have to create an index() method, which will load all the attributes. For that add the below code in your AttributeController.

public function index()
{
    $attributes = $this->attributeRepository->listAttributes();

    $this->setPageTitle('Attributes', 'List of all attributes');
    return view('admin.attributes.index', compact('attributes'));
}

As you can see, firstly we are loading all attributes using the attribute repository, then setting thr page titile and subtitle and finally returning a view named index.blade.php.

Create a new file in resources/views/admin/attributes folder and name it index.blade.php.

Now, copy paste the below code in it.

@extends('admin.app')
@section('title') {{ $pageTitle }} @endsection
@section('content')
    <div class="app-title">
        <div>
            <h1><i class="fa fa-tags"></i> {{ $pageTitle }}</h1>
            <p>{{ $subTitle }}</p>
        </div>
        <a href="{{ route('admin.attributes.create') }}" class="btn btn-primary pull-right">Add Attribute</a>
    </div>
    @include('admin.partials.flash')
    <div class="row">
        <div class="col-md-12">
            <div class="tile">
                <div class="tile-body">
                    <table class="table table-hover table-bordered" id="sampleTable">
                        <thead>
                        <tr>
                            <th> Code </th>
                            <th> Name </th>
                            <th class="text-center"> Frontend Type </th>
                            <th class="text-center"> Filterable </th>
                            <th class="text-center"> Required </th>
                            <th style="width:100px; min-width:100px;" class="text-center text-danger"><i class="fa fa-bolt"> </i></th>
                        </tr>
                        </thead>
                        <tbody>
                            @foreach($attributes as $attribute)
                                <tr>
                                    <td>{{ $attribute->code }}</td>
                                    <td>{{ $attribute->name }}</td>
                                    <td>{{ $attribute->frontend_type }}</td>
                                    <td class="text-center">
                                        @if ($attribute->is_filterable == 1)
                                            <span class="badge badge-success">Yes</span>
                                        @else
                                            <span class="badge badge-danger">No</span>
                                        @endif
                                    </td>
                                    <td class="text-center">
                                        @if ($attribute->is_required == 1)
                                            <span class="badge badge-success">Yes</span>
                                        @else
                                            <span class="badge badge-danger">No</span>
                                        @endif
                                    </td>
                                    <td class="text-center">
                                        <div class="btn-group" role="group" aria-label="Second group">
                                            <a href="{{ route('admin.attributes.edit', $attribute->id) }}" class="btn btn-sm btn-primary"><i class="fa fa-edit"></i></a>
                                            <a href="{{ route('admin.attributes.delete', $attribute->id) }}" class="btn btn-sm btn-danger"><i class="fa fa-trash"></i></a>
                                        </div>
                                    </td>
                                </tr>
                            @endforeach
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
@endsection
@push('scripts')
    <script type="text/javascript" src="{{ asset('backend/js/plugins/jquery.dataTables.min.js') }}"></script>
    <script type="text/javascript" src="{{ asset('backend/js/plugins/dataTables.bootstrap.min.js') }}"></script>
    <script type="text/javascript">$('#sampleTable').DataTable();</script>
@endpush

In this view, we are looping through the $attributes variable a listing all attributes.

Visit the /admin/attributes route in your browser and you will be presented a page like below.

Attributes Section - Index Page
Attributes Section – Index Page

Creating Attributes Create Page

In this section, we will create a Create page, from where we will be able to create a new attribute. Open the AttributeController and add below function in it.

public function create()
{
    $this->setPageTitle('Attributes', 'Create Attribute');
    return view('admin.attributes.create');
}

Create a new file in the resources/views/admin/attributes folder and name it create.blade.php. Add the below HTML markup in this file.

@extends('admin.app')
@section('title') {{ $pageTitle }} @endsection
@section('content')
    <div class="app-title">
        <div>
            <h1><i class="fa fa-cogs"></i> {{ $pageTitle }}</h1>
        </div>
    </div>
    @include('admin.partials.flash')
    <div class="row user">
        <div class="col-md-3">
            <div class="tile p-0">
                <ul class="nav flex-column nav-tabs user-tabs">
                    <li class="nav-item"><a class="nav-link active" href="#general" data-toggle="tab">General</a></li>
                </ul>
            </div>
        </div>
        <div class="col-md-9">
            <div class="tab-content">
                <div class="tab-pane active" id="general">
                    <div class="tile">
                        <form action="{{ route('admin.attributes.store') }}" method="POST" role="form">
                            @csrf
                            <h3 class="tile-title">Attribute Information</h3>
                            <hr>
                            <div class="tile-body">
                                <div class="form-group">
                                    <label class="control-label" for="code">Code</label>
                                    <input
                                        class="form-control"
                                        type="text"
                                        placeholder="Enter attribute code"
                                        id="code"
                                        name="code"
                                        value="{{ old('code') }}"
                                    />
                                </div>
                                <div class="form-group">
                                    <label class="control-label" for="name">Name</label>
                                    <input
                                        class="form-control"
                                        type="text"
                                        placeholder="Enter attribute name"
                                        id="name"
                                        name="name"
                                        value="{{ old('name') }}"
                                    />
                                </div>
                                <div class="form-group">
                                    <label class="control-label" for="frontend_type">Frontend Type</label>
                                    @php $types = ['select' => 'Select Box', 'radio' => 'Radio Button', 'text' => 'Text Field', 'text_area' => 'Text Area']; @endphp
                                    <select name="frontend_type" id="frontend_type" class="form-control">
                                        @foreach($types as $key => $label)
                                            <option value="{{ $key }}">{{ $label }}</option>
                                        @endforeach
                                    </select>
                                </div>
                                <div class="form-group">
                                    <div class="form-check">
                                        <label class="form-check-label">
                                            <input class="form-check-input" type="checkbox" id="is_filterable" name="is_filterable"/>Filterable
                                        </label>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <div class="form-check">
                                        <label class="form-check-label">
                                            <input class="form-check-input" type="checkbox" id="is_required" name="is_required"/>Required
                                        </label>
                                    </div>
                                </div>
                            </div>
                            <div class="tile-footer">
                                <div class="row d-print-none mt-2">
                                    <div class="col-12 text-right">
                                        <button class="btn btn-success" type="submit"><i class="fa fa-fw fa-lg fa-check-circle"></i>Save Attribute</button>
                                        <a class="btn btn-danger" href="{{ route('admin.attributes.index') }}"><i class="fa fa-fw fa-lg fa-arrow-left"></i>Go Back</a>
                                    </div>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

In this file, we added a form with the required fields for our attributes. This form is poiting to admin.attributes.store route. So let’s create this function in our AttributeController controller.

public function store(Request $request)
{
    $this->validate($request, [
        'code'          =>  'required',
        'name'          =>  'required',
        'frontend_type' =>  'required'
    ]);

    $params = $request->except('_token');

    $attribute = $this->attributeRepository->createAttribute($params);

    if (!$attribute) {
        return $this->responseRedirectBack('Error occurred while creating attribute.', 'error', true, true);
    }
    return $this->responseRedirect('admin.attributes.index', 'Attribute added successfully' ,'success',false, false);
}

In this method, we are making some form validation and then passing all data to our AttributeRepository’s createAttribute() method.

Now visit the /admin/attributes/create route in your browser and you will be presented with the below page.

Attributes Section - Create Page
Attributes Section – Create Page

Try to enter some data in this form and hit save attribute button. Your form should be successfully submitted and you will be able to see a new attribute added to your attributes table.

Creating Attributes Edit Page

Now, we will add the edit attribute functionality in our admin area. Add the below edit() method in your AttributeController class.

public function edit($id)
{
    $attribute = $this->attributeRepository->findAttributeById($id);

    $this->setPageTitle('Attributes', 'Edit Attribute : '.$attribute->name);
    return view('admin.attributes.edit', compact('attribute'));
}

You can see in this method we are finding the attribute by id and then passing it on to our edit.blade.php view.

Create a new file called edit.blade.php in resources/views/admin/attributes folder. Add the below markup in this file.

@extends('admin.app')
@section('title') {{ $pageTitle }} @endsection
@section('content')
    <div class="app-title">
        <div>
            <h1><i class="fa fa-cogs"></i> {{ $pageTitle }}</h1>
        </div>
    </div>
    @include('admin.partials.flash')
    <div class="row user">
        <div class="col-md-3">
            <div class="tile p-0">
                <ul class="nav flex-column nav-tabs user-tabs">
                    <li class="nav-item"><a class="nav-link active" href="#general" data-toggle="tab">General</a></li>
                </ul>
            </div>
        </div>
        <div class="col-md-9">
            <div class="tab-content">
                <div class="tab-pane active" id="general">
                    <div class="tile">
                        <form action="{{ route('admin.attributes.update') }}" method="POST" role="form">
                            @csrf
                            <h3 class="tile-title">Attribute Information</h3>
                            <hr>
                            <div class="tile-body">
                                <div class="form-group">
                                    <label class="control-label" for="code">Code</label>
                                    <input
                                        class="form-control"
                                        type="text"
                                        placeholder="Enter attribute code"
                                        id="code"
                                        name="code"
                                        value="{{ old('code', $attribute->code) }}"
                                    />
                                </div>
                                <input type="hidden" name="id" value="{{ $attribute->id }}">
                                <div class="form-group">
                                    <label class="control-label" for="name">Name</label>
                                    <input
                                        class="form-control"
                                        type="text"
                                        placeholder="Enter attribute name"
                                        id="name"
                                        name="name"
                                        value="{{ old('name', $attribute->name) }}"
                                    />
                                </div>
                                <div class="form-group">
                                    <label class="control-label" for="frontend_type">Frontend Type</label>
                                    @php $types = ['select' => 'Select Box', 'radio' => 'Radio Button', 'text' => 'Text Field', 'text_area' => 'Text Area']; @endphp
                                    <select name="frontend_type" id="frontend_type" class="form-control">
                                        @foreach($types as $key => $label)
                                            @if ($attribute->frontend_type == $key)
                                                <option value="{{ $key }}" selected>{{ $label }}</option>
                                            @else
                                                <option value="{{ $key }}">{{ $label }}</option>
                                            @endif
                                        @endforeach
                                    </select>
                                </div>
                                <div class="form-group">
                                    <div class="form-check">
                                        <label class="form-check-label">
                                            <input class="form-check-input"
                                                   type="checkbox"
                                                   id="is_filterable"
                                                   name="is_filterable"
                                                {{ $attribute->is_filterable == 1 ? 'checked' : '' }}/>Filterable
                                        </label>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <div class="form-check">
                                        <label class="form-check-label">
                                            <input class="form-check-input"
                                                   type="checkbox"
                                                   id="is_required"
                                                   name="is_required"
                                                {{ $attribute->is_required == 1 ? 'checked' : '' }}/>Required
                                        </label>
                                    </div>
                                </div>
                            </div>
                            <div class="tile-footer">
                                <div class="row d-print-none mt-2">
                                    <div class="col-12 text-right">
                                        <button class="btn btn-success" type="submit"><i class="fa fa-fw fa-lg fa-check-circle"></i>Update Attribute</button>
                                        <a class="btn btn-danger" href="{{ route('admin.attributes.index') }}"><i class="fa fa-fw fa-lg fa-arrow-left"></i>Go Back</a>
                                    </div>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Next, we will add the update() method in our attribute controller.

public function update(Request $request)
{
    $this->validate($request, [
        'code'          =>  'required',
        'name'          =>  'required',
        'frontend_type' =>  'required'
    ]);

    $params = $request->except('_token');

    $attribute = $this->attributeRepository->updateAttribute($params);

    if (!$attribute) {
        return $this->responseRedirectBack('Error occurred while updating attribute.', 'error', true, true);
    }
    return $this->responseRedirectBack('Attribute updated successfully' ,'success',false, false);
}

Now if you edit any attribute from the attributes list page, you will have fully working edit page like below.

Attributes Section - Edit Page
Attributes Section – Edit Page

Deleting Attribute

Final functionality left for attributes section is the delete() method. Add the below function in you attribute controller.

public function delete($id)
{
    $attribute = $this->attributeRepository->deleteAttribute($id);

    if (!$attribute) {
        return $this->responseRedirectBack('Error occurred while deleting attribute.', 'error', true, true);
    }
    return $this->responseRedirect('admin.attributes.index', 'Attribute deleted successfully' ,'success',false, false);
}

You can test this functionality, by clicking the delete button on the attributes list page.

Conclusion

That’s it for now, in the next post we will start enhancing the edit page of attributes section, from where we can define the attribute values.

Code Repository

You can find the code base of this series on Laravel eCommerce Application repository.

If you have any question about this post, please leave a comment in the comment box below.

9 comments on “Laravel E-Commerce Application Development – Attributes Section Part 2

  1. i get this when updating attributes;

    Method App\Http\Controllers\Admin\AttributeController::setFlashMessages does not exist.

  2. Thanks for the lesson!! When will be the next chapter?? If Possible please make a single lesson for the crud things because the process is repetitive.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.

What's New in Laravel 5.8
Laravel 5 was released in February 2015 and current Laravel 5.7 was released in September 2018. We should be expecting...
Using Scopes in Laravel
Sometimes you run into a situation when you have to reuse some of the conditions more often. Laravel provides a...