Adrian Enache
Adrian Enache

Reputation: 93

Custom field/column attribute for a model with relationships

I'm trying to figure out how to have to make a custom attribute when I'm setting up fields with relationship.

So I have students who are having a hasMany relationship with promotions. Now my problem is when the attribute that I want is in another model/table. For example I need the belt color, but the belt color is not stored in promotions.. promotion table holds just the belt_id. I need to extract the color of that belt from the belts table. I have all the relationships set up but I don't know how to manipulate the attribute. I hope this makes sense.

$this->crud->addField([
    'label' => "Promotions",
    'type' => 'select2_multiple',
    'name' => 'promotion',
    'entity' => 'promotion', 
    'attribute' => 'name', // <- how can I make a custom query for this?
    'pivot' => true, 
]);

Thank you!

Upvotes: 0

Views: 1396

Answers (1)

Adrian Enache
Adrian Enache

Reputation: 93

It looks like I found a solution and fixed a little bug.. at least It looks like to be a bug cuz I can't find the documentation for this feature.. I found it by inspecting the code. Anyway the cool thing is that we can chain relationships with ".", so the column setup will look like this:

$this->crud->addColumn([
   'label' => "Rank", // Table column heading
   'type' => "select",
   'name' => 'promotion_id', // the column that contains the ID of that connected entity;
   'entity' => 'promotion.belt', // <- here I'm chaining the model relationships
   'attribute' => 'color', // the belt's attribute
   'model' => "App\Models\Promotion" // foreign key model
]);

In theory everything should work fine but there is a lil bug in a method located in vendore\backpack\crud\src\CrudPanel.php:

private function getRelationModelInstances($model, $relationString)
{
    $relationArray = explode('.', $relationString);
    $firstRelationName = array_first($relationArray);

    // this is the line with the bug
    //$relation = $model->{$firstRelationName};

    // Fix the bug above
    if($model instanceof Collection) {
        $relation = $model->{$firstRelationName};
    } else {
        $relation = $model[$firstRelationName];
    }

    $results = [];
    if (! empty($relation)) {
        if ($relation instanceof Collection) {
            $currentResults = $relation->toArray();
        } else {
            $currentResults[] = $relation;
        }

        array_shift($relationArray);

        if (! empty($relationArray)) {
            foreach ($currentResults as $currentResult) {
                $results = array_merge($results, $this->getRelationModelInstances($currentResult, implode('.', $relationArray)));
            }
        } else {
            $results = $currentResults;
        }
    }


    return $results;
}

Basically this recursive function when is calling itself it passes the $model parameter as an array not object.. so when it s trying to get $firstRelationName from the $model like this $model->{$firstRelationName} will result in an error.


Meanwhile I discovered a better solution. To accomplish the same goal, I used accessors.

In my promotion model I have:

    /*
    |--------------------------------------------------------------------------
    | RELATIONS
    |--------------------------------------------------------------------------
    */
     public function belt()
    {
        return $this->belongsTo('App\Models\Belt');
    }

    /*
    |--------------------------------------------------------------------------
    | ACCESORS
    |--------------------------------------------------------------------------
    */
    public function getPromotionSummaryAttribute()
    {
        return $this->belt()->get()[0]->color . ', ' . $this->stripe . ' stripe';
    }

And for students I have:

    /*
    |--------------------------------------------------------------------------
    | RELATIONS
    |--------------------------------------------------------------------------
    */
    public function promotion()
    {
        return $this->hasMany('App\Models\Promotion');
    }

    /*
    |--------------------------------------------------------------------------
    | ACCESORS
    |--------------------------------------------------------------------------
    */

    public function GetLastPromotionAttribute()
    {
        return $this->promotion()->get()->last()->promotion_summary;
    }

And the column setup will be simple like this:

   $this->crud->addColumn([
        'name' => 'last_promotion', 
        'type' => 'text', 
        'label' => 'Rank'
   ]);

I hope this will help someone out there :)

Upvotes: 2

Related Questions