skip to Main Content

I have a strange Laravel 9 setup due to being constrained to a very ancient database.

I’m trying to come up with a clean way to create a hasMany relationship to multiple models located in a folder. I believe it would be easiest explained with diagrams:

app/Models/
- Customer
app/Models/Records/
- Orange
- Green
- Blue

Now, all of these records has some connection to customer, but these are all different as well, for example, orange refers to customer using cid, where Green might use customerid.

I’ve already set up logic where every record model has a customer belongsTo relationship depending on the different field names.

public function customer()
    {
        return $this->belongsTo('AppModelsCustomer', 'CustomerId');
    }

I need to create a records() function in the Customer model, that pulls in all of these Records where found. I can’t create any new tables that would be stored on the DB.

2

Answers


  1. Chosen as BEST ANSWER

    None of these solutions quite achieved what I was looking for in terms of simplicity, and in my case my database is quite out of date and slow, so I ended up landing on a solution that is quite simple and faster than everything posted for my use case:

    public function records()
    {
      return [
          "Orange" => $this->hasMany(AppModelsRecordsOrange::class, 'CustomerId', 'Id')->get(),
          "Blue" => $this->hasMany(AppModelsRecordsBlue::class, 'Customerid', 'Id')->get(),
          "Purple" => $this->hasMany(AppModelsRecordsPurple::class, 'customerid', 'Id')->get(),
          "Black" => $this->hasMany(AppModelsRecordsBlack::class, 'CustomerId', 'Id')->get(),
          "Green" => $this->hasMany(AppModelsRecordsGreen::class, 'Customerid', 'Id')->get(),
          "Cyan" => $this->hasMany(AppModelsRecordsCyan::class, 'CustomerId', 'Id')->get()
        ];
    }
    

    This achieves what I was looking for in terms of setting up the relationships when the customerId field was subject to change on the other tables, and is pretty readable overall.


  2. For multiple tables (models) to be connected as hasMany is probably not possible out of the box in Laravel.

    However if you do really need to combine them all, I did once but it is not pretty.

    First add following to each of your AppRecordsModel in example is Green

    class Green extends Model
    {
       protected $table = 'GreenRecords';
    
       ....
       ....
    
       // this is to append/add extra fields into your model
       // that not exist inside the table it self
       protected $appends = [
            'product_data',
            'stuffs',
       ];
    
       // relationship to AppModelsCustomer
       public function customer()
       {
            return $this->belongsTo(Customer::class, 'customerid', 'id');
       }
    
       // you can use it later as Green::query()->customer($id)->get()
       public function scopeCustomer($query, $id)
       {
            return $query->where('customerid', $id);
       }
       
       // you can add here as many data as you like from table GreenRecords, 
       // this will make Green Orange and Blue as if they have common fields
       // you can also separate them by adding add each custom field to $appends
       public function getProductDataAttribute()
       {
            return [
               'name' => $this->name,
               'color' => $this->color,
               'stuffs' => $this->stuffs,
               'other_stuffs' => $this->other_stuffs,
            ];
       }
       public function getStuffsAttribute()
       {
            return $this->stuffs;
       }
       
    }
    

    And now for the Customer model

    class Customer extends Model
    {
        // your Customer relation to Orange
        public function oranges()
        {
           return $this->hasMany(Orange::class, 'cid', 'id');
        }
    
        // your Customer relation to Green
        public function greens()
        {
           return $this->hasMany(Green::class, 'customerid', 'id');
        }
    
        public function getRecords(): Collection
        {
           // we can not use collection merge due to same 'id' in Orange and Green
           $collections = collect();
    
           if ($oranges = Orange::query()
               ->customer($this->id)
               ->get()
           ) {
               foreach ($oranges as $record) {
                   $collections->push($record);
               }
           }
    
           if ($greens = Green::query()
               ->customer($this->id)
               ->get()
            ) {
               foreach ($greens as $record) {
                   $collections->push($record);
               }
           }
    
           return $collections;
        }
    }
    

    So now you can do

    $customer = Customer::find(3);
    $records = $customer->getRecords(); // result will be a collection
    

    And inside your blade you can access them

    @foreach ($records as $record) 
       {{ $record->stuffs }}
       {{ $records->product_data['other_stuffs'] }}
    @endforeach
    

    A collection can be filtered and sorted

    $records->where('product_data.stuffs', 'abc')->sortBy('name');
    

    The only problem here is the records id where Orange can have the same id as Green and Blue.
    Best is to add new field into Orange Green and Blue
    $table->uuid('record_id')->nullable();

    Hope this can help you out.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search