Skip to content
This repository has been archived by the owner on Jul 16, 2021. It is now read-only.

[Proposal] Prefixed Eloquent Models #151

Closed
arcanedev-maroc opened this issue Aug 1, 2016 · 5 comments
Closed

[Proposal] Prefixed Eloquent Models #151

arcanedev-maroc opened this issue Aug 1, 2016 · 5 comments

Comments

@arcanedev-maroc
Copy link

arcanedev-maroc commented Aug 1, 2016

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 👍

@JosephSilber
Copy link
Member

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);
    }
}

@arcanedev-maroc
Copy link
Author

arcanedev-maroc commented Aug 2, 2016

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();
    }
}

@ajcastro
Copy link

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.

@JohnathonKoster
Copy link

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;

}

@SocolaDaiCa
Copy link

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants