Related List

Definition

A related list allow you to link some records to another one. For example, a customer can be linked to several invoices.

Uccello allows to displayed all related records using Related Lists.

You can use 2 types of related lists : n-1 and n-n.

n-1

A Related List n-1 in a module, is linked to an entity field from another module. To be more explicit, let's take an example: we want to see all invoices related to a customer.

To be able to link an invoice to a customer you must create an entity field into the customer module.

// database/migrations/xxxx_xx_xx_xxxxxx_create_invoice_module.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Uccello\Core\Database\Migrations\Migration;
use Uccello\Core\Models\Field;
...
  
class CreateInvoiceModule extends Migration
{
  public function up()
  {
    ...
    $this->createTable();
    $this->createTabsBlocksFields($module);
  }
  
  protected function createTable()
  {
    Schema::create('invoices', function (Blueprint $table) {
      ...
      $table->unsignedInteger('customer_id')->nullable();
      ...
    });
  }
  
  protected function createTabsBlocksFields($module)
  {
    ...
      
    // Field customer
    $field = Field::create([
        'module_id' => $module->id,
        'block_id' => $block->id,
        'name' => 'customer',
        'uitype_id' => uitype('entity')->id,
        'displaytype_id' => displaytype('everywhere')->id,
        'sequence' => $block->fields()->count(),
        'data' => [ "rules" => "required", "module" => "customer" ] // Linked to the customer module
    ]);
  }
  
  ...
}

We will create a Related List n-1 and link it to the field freshly created.

// database/migrations/xxxx_xx_xx_xxxxxx_create_customer_module.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Uccello\Core\Database\Migrations\Migration;
use Uccello\Core\Models\Module;
use Uccello\Core\Models\Field;
use Uccello\Core\Models\Relatedlist;
...
  
class CreateCustomerModule extends Migration
{
  public function up()
  {
    ...
    $this->createRelatedLists($module);
  }
  
  protected function createRelatedLists($module)
  {
        $relatedModule = Module::where('name', 'invoice')->first();

        Relatedlist::create([
            'module_id' => $module->id,
            'related_module_id' => $relatedModule->id,
            'related_field_id' => $relatedModule->fields->where('name', 'customer')->first()->id, // Retrieve the field created before
            'tab_id' => null,
            'label' => 'relatedlist.invoices', // Will be translated
            'type' => 'n-1',
            'method' => 'getDependentList',
            'sequence' => $module->relatedlists()->count(),
            'data' => [ 'actions' => [ 'add' ] ]
        ]);
  }
  
  ...
}

Don't forget to add the new translation:

// resources/lang/en/customer.php

return [
  ...
  'relatedlist' => [
    'invoices' => 'Invoices',
  ],
];

Now, if you visit a customer record, a new tab will display all the invoices related to it. You can create new ones and they will be directly linked to the customer.

n-n

A Related List n-n allows to link a same record to others records from different modules. To be more explicit, let's take an example: we want to link several documents to several customers.

To be able to do this we need to create an association table:

// database/migrations/xxxx_xx_xx_xxxxxx_create_rl_customers_documents_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateRlCustomersDocumentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('rl_customers_documents', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('customer_id');
            $table->unsignedInteger('document_id');
            $table->timestamps();

            // Foreign keys
            $table->foreign('customer_id')
                    ->references('id')->on('customers')
                    ->onDelete('cascade');

            $table->foreign('document_id')
                    ->references('id')->on('documents')
                    ->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('rl_customers_documents');
    }
}

We will create a Related List n-n:

// database/migrations/xxxx_xx_xx_xxxxxx_create_customer_module.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Uccello\Core\Database\Migrations\Migration;
use Uccello\Core\Models\Module;
use Uccello\Core\Models\Field;
use Uccello\Core\Models\Relatedlist;
...
  
class CreateCustomerModule extends Migration
{
  public function up()
  {
    ...
    $this->createRelatedLists($module);
  }
  
  protected function createRelatedLists($module)
  {
    ...
      
    $documentModule = Module::where('name', 'document')->first();
    Relatedlist::create([
      'module_id' => $module->id,
      'related_module_id' => $documentModule->id,
      'tab_id' => null,
      'label' => 'relatedlist.documents', // Will be translated
      'type' => 'n-n',
      'method' => 'getRelatedList',
      'sequence' => $module->relatedlists()->count(),
      'data' => [ 'actions' => [ 'add', 'select' ] ]
    ]);
  }
  
  ...
}

Don't forget to add the new translation:

// resources/lang/en/customer.php

return [
  ...
  'relatedlist' => [
    ...
    'documents' => 'Documents',
  ],
];

Now we'll configure the models:

// app/Customer.php

<?php

namespace App;

use Uccello\Core\Database\Eloquent\Model;
...

class Customer extends Model
{
  public function documents()
    {
        return $this->belongsToMany(Document::class, 'rl_customers_documents')->withTimestamps();
    }
}
// app/Document.php

<?php

namespace App;

use Uccello\Core\Database\Eloquent\Model;
...

class Document extends Model
{
  public function customers()
    {
        return $this->belongsToMany(Customer::class, 'rl_customers_documents')->withTimestamps();
    }
}

Now, if you visit a customer record, a new tab will display all the document related to it. You can select existant documents to link to the record or create new ones.

Options

Display in a Tab or a Block?

By default, a Related List is displayed in a Tab. If you prefer, you can display it in an existant tab. The Related List will be displayed like a new block at the bottom of the page.

To do this, simply fill in tab_id with a valid tab's id from the current module.

protected function createRelatedLists($module)
{
    $documentModule = Module::where('name', 'document')->first();
    Relatedlist::create([
      'module_id' => $module->id,
      'related_module_id' => $documentModule->id,
      'tab_id' => $module->tabs->first()->id, // The Related List will be displayed as a new block in the first tab
      'label' => 'relatedlist.documents', // Will be translated
      'type' => 'n-n',
      'method' => 'getRelatedList',
      'sequence' => $module->relatedlists()->count(),
      'data' => [ 'actions' => [ 'add', 'select' ] ]
    ]);
}

Properties

You can create your own method for retrieving the related records. To do this you have to create 2 functions in the model in which add the related list.

  • myOwnMethod: You can use the name of you want

  • myOwnMethodCount: The same name as the method above, suffixed by Count

Example

<?php

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Uccello\Core\Models\Relatedlist;
use Uccello\Core\Support\Traits\UccelloModule;

class Person extends Model
{
    use UccelloModule;

    ...

    /**
     * Retrieves related records for n-n relations
     *
     * @param Relatedlist $relatedList
     * @param integer $recordId
     * @param Builder|null $query
     * @param int $start
     * @return Collection
     */
    public function myOwnMethod(Relatedlist $relatedList, int $recordId, ?Builder $query = null)
    {
        $modelClass = $relatedList->module->model_class;
        $relationName = $relatedList->relationName;
    
        $record = $modelClass::find($recordId);
        $filter = ['order' => request('order')];
    
        $query = $record->$relationName()
                        ->filterBy($filter);
    
        return $query;
    }
    
    /**
     * Counts related records for n-n relations
     *
     * @param Relatedlist $relatedList
     * @param integer $recordId
     * @return int
     */
    public function myOwnMethodCount(Relatedlist $relatedList, int $recordId) : int
    {
        return $this->myOwnMethod($relatedList, $recordId)->count();
    }
}

Last updated