Laravel E-Commerce Application Development – Brands Section

Laravel E-Commerce Application Development – Brands Section

Laravel E-Commerce Application Development ( 19 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
  15. Laravel E-Commerce Application Development – Products Section Part 1
  16. Laravel E-Commerce Application Development – Products Section Part 2
  17. Laravel E-Commerce Application Development – Products Section Part 3
  18. Laravel E-Commerce Application Development – Products Section Part 4
  19. Laravel E-Commerce Application Development – Frontend Login & Registration

This is part 13 of the Laravel E-Commerce Application Development series. In this part, we will add the brands’ section to our backend panel.

As this section work will be much similar to the categories section, I will keep it very simple.

Adding Routes

Firstly, we will add the required routes for our Brands section. For this, open the admin.php file from the routes folder and add the below routes right after the attributes routes.

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

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

});

Adding Sidebar Link

First thing first, we need to add the brands’ link to our menu in the 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 dashboard link.

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

Creating Brand Controller

We have already added the routes to our admin.php file, which all points to the BrandController. Let’s create this controller using the below command.

php artisan make:controller Admin\BrandController

Above command will generate the BrandController in app/Http/Controllers/Admin folder, open this file and replace the content with the below one.

namespace App\Http\Controllers\Admin;

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

class BrandController extends BaseController
{
    //
}

We will leave this controller as it is and will be adding the required methods in the upcoming sections.

Creating Brand Model and Migration

To save our brands data into the database, we will need to create a migration and model for Brand. Run the below command in the terminal to generate a model and migration.

php artisan make:model Models\Brand -m

The -m flag will create a migration file for our Brand model. Above command will generate a Brand model in app/Models folder and a migration file for this model in database/migrations folder.

Open your migration file and update with the below class.

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateBrandsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('brands', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name', 45);
            $table->string('slug');
            $table->string('logo')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('brands');
    }
}

Now run the below command to generate the brands table in your database.

php artisan migrate

Above migration will create the brands’ table with name and logo it’s fields.

Now, open you Brand model from app/Models folder and update the whole class with the below one.

namespace App\Models;

use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Model;

/**
 * Class Brand
 * @package App\Models
 */
class Brand extends Model
{
    /**
     * @var string
     */
    protected $table = 'brands';

    /**
     * @var array
     */
    protected $fillable = ['name', 'slug', 'logo'];

    /**
     * @param $value
     */
    public function setNameAttribute($value)
    {
        $this->attributes['name'] = $value;
        $this->attributes['slug'] = Str::slug($value);
    }
}

In the above model class, we have defined the table for our brands and fillable property for mass assignment.

Creating Brand Contract and Repository

Now, we will create an interface for our Brand model. Create a new file called BrandContract.php in app/Contracts folder and add the below code block in it.

namespace App\Contracts;

/**
 * Interface BrandContract
 * @package App\Contracts
 */
interface BrandContract
{
    /**
     * @param string $order
     * @param string $sort
     * @param array $columns
     * @return mixed
     */
    public function listBrands(string $order = 'id', string $sort = 'desc', array $columns = ['*']);

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

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

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

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

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

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

namespace App\Repositories;

use App\Models\Brand;
use App\Traits\UploadAble;
use Illuminate\Http\UploadedFile;
use App\Contracts\BrandContract;
use Illuminate\Database\QueryException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Doctrine\Instantiator\Exception\InvalidArgumentException;

/**
 * Class CategoryRepository
 *
 * @package \App\Repositories
 */
class BrandRepository extends BaseRepository implements BrandContract
{
    use UploadAble;

    /**
     * CategoryRepository constructor.
     * @param Brand $model
     */
    public function __construct(Brand $model)
    {
        parent::__construct($model);
        $this->model = $model;
    }

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

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

        } catch (ModelNotFoundException $e) {

            throw new ModelNotFoundException($e);
        }

    }

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

            $logo = null;

            if ($collection->has('logo') && ($params['logo'] instanceof  UploadedFile)) {
                $logo = $this->uploadOne($params['logo'], 'brands');
            }

            $merge = $collection->merge(compact('logo'));

            $brand = new Brand($merge->all());

            $brand->save();

            return $brand;

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

    /**
     * @param array $params
     * @return mixed
     */
    public function updateBrand(array $params)
    {
        $brand = $this->findBrandById($params['id']);

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

        if ($collection->has('logo') && ($params['logo'] instanceof  UploadedFile)) {

            if ($brand->logo != null) {
                $this->deleteOne($brand->logo);
            }

            $logo = $this->uploadOne($params['logo'], 'brands');
        }

        $merge = $collection->merge(compact('logo'));

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

        return $brand;
    }

    /**
     * @param $id
     * @return bool|mixed
     */
    public function deleteBrand($id)
    {
        $brand = $this->findBrandById($id);

        if ($brand->logo != null) {
            $this->deleteOne($brand->logo);
        }

        $brand->delete();

        return $brand;
    }
}

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

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

use App\Contracts\BrandContract;
use App\Repositories\BrandRepository;

Next, update the $repositories property like below.

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

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

Open the BrandController file and update with the below one.

namespace App\Http\Controllers\Admin;

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

class BrandController extends BaseController
{
    /**
     * @var BrandContract
     */
    protected $brandRepository;

    /**
     * CategoryController constructor.
     * @param BrandContract $brandRepository
     */
    public function __construct(BrandContract $brandRepository)
    {
        $this->brandRepository = $brandRepository;
    }
}

Creating Brand Listing Page

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

/**
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function index()
{
    $brands = $this->brandRepository->listBrands();

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

As you can see, firstly we are loading all brands using the brand repository, then setting the page title and subtitle and finally returning a view named index.blade.php.

Create a new file in resources/views/admin/brands 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-briefcase"></i> {{ $pageTitle }}</h1>
            <p>{{ $subTitle }}</p>
        </div>
        <a href="{{ route('admin.brands.create') }}" class="btn btn-primary pull-right">Add Brand</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> # </th>
                            <th> Name </th>
                            <th> Slug </th>
                            <th style="width:100px; min-width:100px;" class="text-center text-danger"><i class="fa fa-bolt"> </i></th>
                        </tr>
                        </thead>
                        <tbody>
                        @foreach($brands as $brand)
                            <tr>
                                <td>{{ $brand->id }}</td>
                                <td>{{ $brand->name }}</td>
                                <td>{{ $brand->slug }}</td>
                                <td class="text-center">
                                    <div class="btn-group" role="group" aria-label="Second group">
                                        <a href="{{ route('admin.brands.edit', $brand->id) }}" class="btn btn-sm btn-primary"><i class="fa fa-edit"></i></a>
                                        <a href="{{ route('admin.brands.delete', $brand->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 $brands variable passed throught the BrandController which listing all brands.

Creating Brand

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

/**
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function create()
{
    $this->setPageTitle('Brands', 'Create Brand');
    return view('admin.brands.create');
}

Create a new file in the resources/views/admin/brands 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-briefcase"></i> {{ $pageTitle }}</h1>
        </div>
    </div>
    @include('admin.partials.flash')
    <div class="row">
        <div class="col-md-8 mx-auto">
            <div class="tile">
                <h3 class="tile-title">{{ $subTitle }}</h3>
                <form action="{{ route('admin.brands.store') }}" method="POST" role="form" enctype="multipart/form-data">
                    @csrf
                    <div class="tile-body">
                        <div class="form-group">
                            <label class="control-label" for="name">Name <span class="m-l-5 text-danger"> *</span></label>
                            <input class="form-control @error('name') is-invalid @enderror" type="text" name="name" id="name" value="{{ old('name') }}"/>
                            @error('name') {{ $message }} @enderror
                        </div>
                        <div class="form-group">
                            <label class="control-label">Brand Logo</label>
                            <input class="form-control @error('logo') is-invalid @enderror" type="file" id="logo" name="logo"/>
                            @error('logo') {{ $message }} @enderror
                        </div>
                    </div>
                    <div class="tile-footer">
                        <button class="btn btn-primary" type="submit"><i class="fa fa-fw fa-lg fa-check-circle"></i>Save Brand</button>
                        &nbsp;&nbsp;&nbsp;
                        <a class="btn btn-secondary" href="{{ route('admin.brands.index') }}"><i class="fa fa-fw fa-lg fa-times-circle"></i>Cancel</a>
                    </div>
                </form>
            </div>
        </div>
    </div>
@endsection

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

/**
 * @param Request $request
 * @return \Illuminate\Http\RedirectResponse
 * @throws \Illuminate\Validation\ValidationException
 */
public function store(Request $request)
{
    $this->validate($request, [
        'name'      =>  'required|max:191',
        'image'     =>  'mimes:jpg,jpeg,png|max:1000'
    ]);

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

    $brand = $this->brandRepository->createBrand($params);

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

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

Now visit the /admin/brands/create route in your browser and you will be presented with form to create a brand.

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

Editing Brand

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

/**
 * @param $id
 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 */
public function edit($id)
{
    $brand = $this->brandRepository->findBrandById($id);

    $this->setPageTitle('Brands', 'Edit Brand : '.$brand->name);
    return view('admin.brands.edit', compact('brand'));
}

You can see in this method we are finding the brand 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/brands 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-briefcase"></i> {{ $pageTitle }}</h1>
        </div>
    </div>
    @include('admin.partials.flash')
    <div class="row">
        <div class="col-md-8 mx-auto">
            <div class="tile">
                <h3 class="tile-title">{{ $subTitle }}</h3>
                <form action="{{ route('admin.brands.update') }}" method="POST" role="form" enctype="multipart/form-data">
                    @csrf
                    <div class="tile-body">
                        <div class="form-group">
                            <label class="control-label" for="name">Name <span class="m-l-5 text-danger"> *</span></label>
                            <input class="form-control @error('name') is-invalid @enderror" type="text" name="name" id="name" value="{{ old('name', $brand->name) }}"/>
                            <input type="hidden" name="id" value="{{ $brand->id }}">
                            @error('name') {{ $message }} @enderror
                        </div>
                        <div class="form-group">
                            <div class="row">
                                    @if ($brand->logo != null)
                                    <div class="col-md-2">
                                        <figure class="mt-2" style="width: 80px; height: auto;">
                                                <img src="{{ asset('storage/'.$brand->logo) }}" id="brandLogo" class="img-fluid" alt="img">
                                        </figure>
                                    </div>
                                @endif
                                <div class="col-md-10">
                                    <label class="control-label">Brand Logo</label>
                                    <input class="form-control @error('logo') is-invalid @enderror" type="file" id="logo" name="logo"/>
                                    @error('logo') {{ $message }} @enderror
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="tile-footer">
                        <button class="btn btn-primary" type="submit"><i class="fa fa-fw fa-lg fa-check-circle"></i>Save Brand</button>
                        &nbsp;&nbsp;&nbsp;
                        <a class="btn btn-secondary" href="{{ route('admin.brands.index') }}"><i class="fa fa-fw fa-lg fa-times-circle"></i>Cancel</a>
                    </div>
                </form>
            </div>
        </div>
    </div>
@endsection

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

/**
 * @param Request $request
 * @return \Illuminate\Http\RedirectResponse
 * @throws \Illuminate\Validation\ValidationException
 */
public function update(Request $request)
{
    $this->validate($request, [
        'name'      =>  'required|max:191',
        'image'     =>  'mimes:jpg,jpeg,png|max:1000'
    ]);

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

    $brand = $this->brandRepository->updateBrand($params);

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

Now if you edit any brand from the brands’ list page, you will have fully working edit page.

Deleting a Brand

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

/**
 * @param $id
 * @return \Illuminate\Http\RedirectResponse
 */
public function delete($id)
{
    $brand = $this->brandRepository->deleteBrand($id);

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

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

Conclusion

That’s it for now, in the next post we will start to continue working on the Product section.

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 and I will try my best to answer your questions.

10 comments on “Laravel E-Commerce Application Development – Brands Section

  1. Hello thanks for this great project! I recovered the project on Github but when I do php artisan migrate, I have this error:
    PDOException: 🙁 “SQLSTATE [42S01]: Base table or view already exists: 1050 ‘attribute_values’ table already exists”)
           /var/www/html/laravel-ecommerce-application/vendor/laravel/framework/src/Illuminate/Database/Connection.php:458

    But I’m only trying to install the application 🙂

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.

Using Scopes in Laravel
Sometimes you run into a situation when you have to reuse some of the conditions more often. Laravel provides a...