laravel / ideas

Issues board used for Laravel internals discussions.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Proposal] Prefixed Eloquent Models

arcanedev-maroc opened this issue · comments

This is useful when i create modules inside my app and separate the the db tables by prefixing each module tables.

For example:

  • shop_* : shop_products, shop_orders, shop_shipping
  • blog_*: blog_posts, blog_comments, blog_categories, blog_tags
  • sys_*: sys_users, sys_roles

So the solution that i found is to override the Illuminate Eloquent Model by doing this (Note: i didn't override the relationships methods like joiningTable(), but you can add the prefix inside the child class):

<?php namespace App\Bases;

use Illuminate\Database\Eloquent\Model as Eloquent;

abstract class Model extends Eloquent
{
    /* ------------------------------------------
     |  Properties
     | ------------------------------------------
     */
    /**
     * The table prefix.
     *
     * @var string|null
     */
    protected $prefix;

    /* ------------------------------------------
     |  Getters & Setters
     | ------------------------------------------
     */
    public function getTable()
    {
        return $this->getPrefix() . parent::getTable();
    }

    public function getPrefix()
    {
        return is_null($this->prefix) ? '' : $this->prefix;
    }

    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;

        return $this;
    }
}

And in a module like Blog (e.g.), i create a base Model Class (abstract) :

<?php namespace App\Modules\Blog\Models;

use App\Bases\Model as BaseModel;

abstract class Model extends BaseModel
{
    /* ------------------------------------------
     |  Properties
     | ------------------------------------------
     */
    /**
     * The table prefix.
     *
     * @var string|null
     */
    protected $prefix = 'blog_';
}

After that, the model itself (Post e.g.):

<?php namespace App\Modules\Blog\Models;

class Post extends Model
{
    // Your normal eloquent stuff
}

So, what is the benefit by using the prefixes ? The idea behind this is to make the code/db modular and/or extract it as a package, i do the same thing for the migration 😄.

And the best part is you can do something like this:

<?php namespace App\Modules\Blog\Models;

use App\Bases\Model as BaseModel;

abstract class Model extends BaseModel
{
    /* ------------------------------------------
     |  Constructor
     | ------------------------------------------
     */
    public function __construct(array $attributes)
    {
        // You can use the config file to set the prefix dynamically for a specific module.
        $this->setPrefix(config('modules.blog.database.prefix', 'blog_'));

        parent::__construct($attributes);
    }
}

So i hope the Illuminate\Database\Eloquent\Model class implement the prefix feature like i did for more flexibility.

Any feedbacks are welcome 👍

How is setting a prefix easier than just setting the table name directly?

Even if you want to keep it in a config somewhere, you can still do this:

class Post extends Model
{
    public function __construct(array $attributes)
    {
        $this->table = config('modules.blog.database.prefix', 'blog_')).$this->getTable();

        parent::__construct($attributes);
    }
}

Yeah i've done it long time ago and it's ugly.

BTW, this is much cleaner:

class Post extends Model
{
    public function getTable()
    {
        return config('modules.blog.database.prefix', 'blog_')).parent::getTable();
    }
}

I like the idea of prefixing table names. This may serve as namespacing in database tables. Easier to navigate related tables. But this feature should be optional.

Something like this can be accomplished using traits pretty simply. Also, using traits would make it an opt-in feature, like soft deletes.

The following code is all based on the code posted by @arcanedev-maroc

For example, the trait HasTablePrefix just contains the getters and setters, and assumes that a $prefix property already exists on the model:

<?php

namespace App;

trait HasTablePrefix
{

    /**
     * Get the table associated with the model.
     * 
     * @return string
     */
    public function getTable()
    {
        return $this->getPrefix() . parent::getTable();
    }

    /**
     * Get the prefix associated with the model.
     * 
     * @return string
     */
    public function getPrefix()
    {
        return is_null($this->prefix) ? '' : $this->prefix;
    }

    /**
     * Set the prefix associated with the model.
     * 
     * @param  string $prefix
     * @return $this
     */
    public function setPrefix($prefix)
    {
        $this->prefix = $prefix;

        return $this;
    }

}

Using it inside a Post model would look like this:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasTablePrefix;

    protected $prefix = 'blog_';

}

However, if someone were to use this approach and had many models with a prefix that had to be updated this could prove to be a pain. We can do better by creating another trait (this trait would theoretically exist in user-land code, not in the core), say something like BlogPrefix:

<?php

namespace App;

trait BlogPrefix
{
    use HasTablePrefix;

    /**
     * The table prefix associated with the model.
     * 
     * @var string
     */
    protected $prefix = 'blog_';

}

the final model might look something like this:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use BlogPrefix;

}

I have created a package for this idea, please help me review it
https://github.com/SocolaDaiCa/laravel-table-prefix