# Build an E-Commerce System in Seconds With TomatoPHP

> Step by step, turn a fresh Laravel app into a full store with TomatoPHP: admin, roles, CRM, wallets, CMS, themes and a ready storefront.

*2026-09-17T03:23:45Z*

Hi, community.

TomatoPHP is a cutting-edge, open-source set of Laravel packages built to streamline development in the VILT stack. The ecosystem uses Splade to generate modern, high-performance single-page applications (SPAs) from nothing but Blade files, and it makes the development experience both efficient and enjoyable.

We keep growing the ecosystem by adding features to our plugins, and we have now built a complete e-commerce system out of them. In this article I will walk you through the whole implementation, step by step, so you can drop a robust e-commerce system into your own Laravel application.

By the end you will have:

- An admin dashboard with role management
- A CRM for your customers, with its own authentication guard
- Customer wallets and payments
- A CMS and a theme system for the storefront
- Products, orders, offers and branches, with a ready-made e-commerce theme on top

_Note: this guide was written in early 2024 for the Laravel 10 era of TomatoPHP. Newer Laravel versions changed some of the files mentioned below (for example `RouteServiceProvider.php` and the `Authenticate` middleware), so adapt those steps if you are on a newer release._

## Install Tomato Admin

Everything starts from the tomato-admin plugin, installed on a fresh Laravel app.

1. Create a new Laravel app:

```bash
composer create-project laravel/laravel tomato
```

> If you don't have an environment for Laravel yet, you can use this [doc](https://docs.tomatophp.com/get-started/environment-setup) to build one on your Ubuntu Linux.

2. `cd` into your project folder, set your database credentials in `.env`, and make sure the app is running and the database is connected. The easiest way to check is to run the migrations:

```bash
php artisan migrate
```

3. Install tomato-admin:

```bash
composer require tomatophp/tomato-admin
```

4. When Composer is done, run the auto-installer:

```bash
php artisan tomato-admin:install
```

5. If you are on macOS the installer can install the Yarn packages for you automatically. If not, just build your assets like this:

```bash
yarn && yarn build
```

_Note: the original command read `yarn & yarn build`; a single `&` sends `yarn` to the background, so `&&` is used here and throughout this guide._

Now tomato-admin is installed on your Laravel project.

### Publish the Media Library migrations

Our packages use media, so we need to publish the Spatie Media Library migrations:

```bash
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-migrations"
```

If that command does not work, run the interactive version instead:

![The interactive vendor:publish prompt listing the publishable providers and tags](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5h8jkfafatf1nqtuh8ib.png)

```bash
php artisan vendor:publish
```

Then type `media` in the search and select the migrations entry. After that, migrate:

```bash
php artisan migrate
```

Check your browser now and you will see a homepage like this:

![The Laravel homepage after installing Tomato Admin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wrm0574dwbhwx43nvxgk.png)

Finally, change the `HOME` const in `RouteServiceProvider.php` to `/admin`, so users are redirected to the admin panel after they authenticate.

## Install Tomato Roles

No e-commerce system is complete without solid role management, so the next package is tomato-roles.

1. Require the package:

```bash
composer require tomatophp/tomato-roles
```

2. When Composer is done, run the installer:

```bash
php artisan tomato-roles:install
```

3. Open `app\Models\User.php` and add this trait to it:

```php
use \Spatie\Permission\Traits\HasRoles;
```

Your dashboard is now ready. Log in at `/admin/login` with `admin@admin.com` and `password` as the password.

![The TomatoPHP admin dashboard after logging in](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3qg11z9vp04z3vc75pgg.png)

> If you try to open any page and get redirected to `Two-factor Confirmation`, and you don't need it for now, you can turn it off by removing `implements MustVerifyEmail` from your `User.php` model.

## Install Tomato CRM

An e-commerce system has to manage customer interactions, customer authentication and other key actions. tomato-crm handles all of that.

1. Require and install the package:

```bash
composer require tomatophp/tomato-crm
```

```bash
php artisan tomato-crm:install
```

2. Publish the `Accounts.php` model into your app so you can customize it:

```bash
php artisan vendor:publish --tag="tomato-crm-model"
```

3. Publish the tomato-crm config:

```bash
php artisan vendor:publish --tag="tomato-crm-config"
```

4. In the `tomato-crm.php` config, point the model to your app's copy:

```php
"model" => \App\Models\Account::class,
```

### Add an accounts guard

Customers log in separately from admins, so we need a new guard. Add it to `config/auth.php` like this:

```php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'accounts' => [
            'driver' => 'session',
            'provider' => 'accounts',
        ]
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | If you have multiple user tables or models you may configure multiple
    | sources which represent each model / table. These sources may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'accounts' => [
            'driver' => 'eloquent',
            'model' => App\Models\Account::class,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | You may specify multiple password reset configurations if you have more
    | than one user table or model in the application and you want to have
    | separate password reset settings based on the specific user types.
    |
    | The expiry time is the number of minutes that each reset token will be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    | The throttle setting is the number of seconds a user must wait before
    | generating more password reset tokens. This prevents the user from
    | quickly generating a very large amount of password reset tokens.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_reset_tokens',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Password Confirmation Timeout
    |--------------------------------------------------------------------------
    |
    | Here you may define the amount of seconds before a password confirmation
    | times out and the user is prompted to re-enter their password via the
    | confirmation screen. By default, the timeout lasts for three hours.
    |
    */

    'password_timeout' => 10800,

];
```

Then clear the config cache and rebuild your assets:

```bash
php artisan config:clear
```

```bash
yarn && yarn build
```

Your CRM is ready. You can check it on your dashboard.

## Install Tomato Wallet

To handle transactions between customers and vendors you need a solid payment handler. tomato-wallet manages customer wallets and handles payments too, and it ships with many integrated payment gateways.

1. Require and install it:

```bash
composer require tomatophp/tomato-wallet
```

```bash
php artisan tomato-wallet:install
```

2. Make your `Account.php` model implement the `Wallet` interface and use the `HasWallet` trait, so customer wallets work. Your Account model should look like this:

```php
<?php

namespace App\Models;

use Bavix\Wallet\Interfaces\Wallet;
use Bavix\Wallet\Traits\HasWallet;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Macroable\Macroable;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\Permission\Traits\HasRoles;
use TomatoPHP\TomatoCrm\Models\Group;

/**
 * @property integer $id
 * @property string $name
 * @property string $username
 * @property string $loginBy
 * @property string $address
 * @property string $type
 * @property string $password
 * @property string $otp_code
 * @property string $otp_activated_at
 * @property string $last_login
 * @property string $agent
 * @property string $host
 * @property integer $attempts
 * @property boolean $login
 * @property boolean $activated
 * @property boolean $blocked
 * @property string $deleted_at
 * @property string $created_at
 * @property string $updated_at
 * @property AccountsMeta[] $accountsMetas
 * @property Activity[] $activities
 * @property Comment[] $comments
 * @property Model meta($key, $value)
 * @property Location[] $locations
 */
class Account extends Authenticatable implements HasMedia, Wallet
{
    use InteractsWithMedia;
    use HasApiTokens, HasFactory, Notifiable;
    use HasWallet;

    /**
     * @var array
     */
    protected $fillable = [
        'email',
        'phone',
        'parent_id',
        'type',
        'name',
        'username',
        'loginBy',
        'address',
        'password',
        'otp_code',
        'otp_activated_at',
        'last_login',
        'agent',
        'host',
        'is_login',
        'is_active',
        'deleted_at',
        'created_at',
        'updated_at'
    ];

    protected $casts = [
        'is_login' => 'boolean',
        'is_active' => 'boolean'
    ];
    protected $dates = [
        'deleted_at',
        'created_at',
        'updated_at',
        'otp_activated_at',
        'last_login',
    ];


    protected $appends = [
        'birthday',
        'gender',
        'more'
    ];

    public function getMoreAttribute()
    {
        $metas = $this->accountsMetas()->get()->pluck('value', 'key')->toArray();
        return $metas;
    }

    public function getBirthdayAttribute()
    {
        return $this->meta('birthday') ?: null;
    }

    public function getGenderAttribute()
    {
        return $this->meta('gender') ?: null;
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function accountsMetas()
    {
        return $this->hasMany('TomatoPHP\TomatoCrm\Models\AccountsMeta');
    }

    /**
     * @param string $key
     * @param string|null $value
     * @return Model|string
     */
    public function meta(string $key, string|null $value=null): Model|string|null
    {
        if($value){
            return $this->accountsMetas()->updateOrCreate(['key' => $key], ['value' => $value]);
        }
        else {
            return $this->accountsMetas()->where('key', $key)->first()?->value;
        }
    }
    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function activities()
    {
        return $this->hasMany('TomatoPHP\TomatoCrm\Models\Activity');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function comments()
    {
        return $this->hasMany('TomatoPHP\TomatoCrm\Models\Comment');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function locations()
    {
        return $this->hasMany('TomatoPHP\TomatoCrm\Models\Location');
    }

    public function groups(){
        return $this->belongsToMany(Group::class, 'account_groups', 'account_id', 'group_id');
    }
}
```

Your wallet is now working. You can start any transaction and check the customer's balance.

## Install Tomato CMS

Every working store needs a well-optimized front end. To improve SEO and add content such as posts and pages, we use tomato-cms:

```bash
composer require tomatophp/tomato-cms
```

```bash
php artisan tomato-cms:install
```

## Install Tomato Themes

The storefront itself is built as a theme with tomato-themes. It makes multi-theme projects simple and uses a Hierarchical Model-View-Controller (HMVC) architecture.

1. Require and install the package, then rebuild your assets:

```bash
composer require tomatophp/tomato-themes
```

```bash
php artisan tomato-themes:install
```

```bash
yarn && yarn build
```

2. Make sure there is a `Themes` folder in your project root, and add the `Themes` namespace to the autoload section of your `composer.json`:

```json
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/",
            "Themes\\": "Themes/"
        }
    },
```

3. Reload Composer's autoloader:

```bash
composer dump-autoload
```

4. Add the theme views to the `content` array in your `tailwind.config.js`, so Tailwind picks up their classes:

```js
content: [
    ...
    "./Themes/**/*.blade.php",
    "./Themes/**/**/*.blade.php",
],
```

Your theme system is ready: you can upload a theme or create a new one.

## Install Tomato E-Commerce

With the foundations in place (CRM, role management and the supporting packages), it's time to bring them together with the e-commerce system itself.

1. Require and install it:

```bash
composer require tomatophp/tomato-ecommerce
```

```bash
php artisan tomato-ecommerce:install
```

This package installs tomato-products, tomato-orders, tomato-offers and tomato-branches for you.

2. To make everything work, install tomato-branches:

```bash
php artisan tomato-branches:install
```

3. Create at least one `Shipping Vendor` from `/admin/shipping-vendors`.

4. Go to `/admin/settings/seo`, upload your logos and change the site name and SEO data.

## Install the e-commerce theme

Now let's make the store and the CMS look good. Our theme has a simple, stylish design that is an ideal starting point for customization.

1. Go into your `Themes` folder:

```bash
cd Themes
```

2. Clone our theme:

```bash
git clone git@github.com:tomatophp/Ecommerce.git
```

3. Open `/themes` in your dashboard. The new theme is listed there, and you just activate it.

> Please note that your main `routes/web.php` must not define a `/` route, because it can override the theme routes.

4. Rebuild your assets to fix the styles:

```bash
yarn && yarn build
```

Check your home page now and you will get something like this:

![The storefront home page rendered by the TomatoPHP e-commerce theme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57tiy1f52bciut39u14q.png)

You can pick any section from the dropdown at the top and add it to your page.

### Build the menus

Build your menus from `/admin/menus` using these endpoints:

- Home `/`
- About `/about`
- Shop `/shop`
- Blog `/blog`
- Contact `/contact`
- Terms & Conditions `/terms`
- Privacy `/privacy`

You can create two menus, `main` and `footer`. The main menu shows up in your header automatically, and the footer menu in your footer.

### Redirect guests to the customer login

Change the `Authenticate` middleware so unauthenticated visitors are redirected to the accounts login route:

```php
<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     */
    protected function redirectTo(Request $request): ?string
    {
        return $request->expectsJson() ? null : route('accounts.login');
    }
}
```

### Add the e-commerce traits to Account

Finally, add these traits to your `Account.php` model:

```php
    use \TomatoPHP\TomatoEcommerce\Services\Traits\InteractsWithEcommerce;
    use \TomatoPHP\TomatoNotifications\Traits\InteractWithNotifications;
    use \TomatoPHP\TomatoOrders\Services\Traits\InteractsWithOrders;
```

## Wrapping up

With the full e-commerce system installed, you can start managing your catalog: add products, define categories to shape your offering, and create orders. The system is ready to handle transactions and run your online business.

Thanks for using Tomato Plugins and the TomatoPHP framework.

- Join the support server on [Discord](https://discord.gg/VZc8nBJ3ZU)
- Read the [docs](https://docs.tomatophp.com)
- If you like any repo, please give it a star on [TomatoPHP GitHub](https://github.com/tomatophp)
- Sponsor us on [GitHub Sponsors](https://github.com/sponsors/fadymondy)

---

Source: https://fadymondy.com/en/blog/build-e-commerce-system-in-seconds-with-tomatophp
