rinvex / laravel-attributes

⚠️ [ABANDONED] Rinvex Attributable is a robust, intelligent, and integrated Entity-Attribute-Value model (EAV) implementation for Laravel Eloquent, with powerful underlying for managing entity attributes implicitly as relations with ease. It utilizes the power of Laravel Eloquent, with smooth and seamless integration.
MIT License
434 stars 104 forks source link

Is it possible to have muti tenant support? #85

Closed iamsayantan closed 5 years ago

iamsayantan commented 5 years ago

Hi, first of all thank you for this awesome work. However our requirements are little different. We are working on a multi tenant application and I was wondering if it is possible to make the custom attribute tenant specific. To give an idea, in our case, a Company is a tenant. A Company can have multiple Branches. Each Company can set up custom attributes for their Branches which should be available only to that company. So will it be possible to have this kind of control with the current project? Any help is welcome. Thank you!

jlstandout commented 5 years ago

Hi, iamsayantan.

We needed something like this. We did override Rinvex\Attributes\Models\Attribute model and added morphTo relationship

use Rinvex\Attributes\Models\Attribute as RinvexAttribute;

class Attribute extends RinvexAttribute {

     ...
     // Make sure to create migration where you add morph fields into attributes table

    public function owner()
    {
        return $this->morphTo('owner');
    }
}

And than in your repository (or model as local scope) you can create methods where you are filtering by owner, what in your case can be company.

    // Somewhere in repository
    public function findOwned($models = [])
    {
        $attributes = $this->model;

        if (!is_array($models)) {
            $models = [$models];
        }

        foreach ($models as $key => $model) {
            if ($key == 0) {
                $attributes = $attributes->where(function ($subquery) use ($model) {
                    $subquery->where('owner_type', get_class($model))
                    ->where('owner_id', $model->id)
                    ;
                });
            } else {
                $attributes = $attributes->orWhere(function ($subquery) use ($model) {
                    $subquery->where('owner_type', get_class($model))
                    ->where('owner_id', $model->id)
                    ;
                });
            }
        }

        return $attributes;
    }

And then search company attributes with: $company_attrs = $this->attributesRepository->findOwned($company)

For branches you can use default package entities.

I hope this will help.

iamsayantan commented 5 years ago

Your solution looks good. We achieved this by adding company_id in attribute_entity table and overrode Rinvex\Attributes\Models\AttributeEntity to add global CompanyScope.

<?php
namespace App\EAVModels;

use App\Scopes\CompanyScope;
use Rinvex\Attributes\Models\AttributeEntity as BaseAttributeEntity;

class AttributeEntity extends BaseAttributeEntity
{
    protected $fillable = [
        'entity_type',
        'company_id'
    ];

    /**
     * The CompanyScope global scope makes sure that all the custom attributes are company
     * specific.
     */
    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope(new CompanyScope);
    }

}

And then overrode Rinvex\Attributes\Models\Attribute to add a mutator which updates the company_id field in attribute_entity table.

<?php

namespace App\EAVModels;

use Rinvex\Attributes\Models\Attribute as BaseAttribute;

class Attribute extends BaseAttribute
{
    protected $fillable = [
        'name',
        'slug',
        'description',
        'sort_order',
        'group',
        'type',
        'is_required',
        'is_collection',
        'default',
        'entities',
        'company_id'
    ];

    /**
     * This mutator sets the company id to the attribute_entity table.
     *
     * @param $companyId
     */
    public function setCompanyIdAttribute($companyId): void
    {
        static::saved(function ($model) use ($companyId) {
            $this->entities()->withoutGlobalScopes()->update(['company_id' => $companyId]);
        });
    }
}
Omranic commented 5 years ago

Nice discussion! I'm curious, do you use specific tenantable package, or custom built solution? You may want to consider also utilizing rinvex/laravel-tenants also 🙂