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

Attribute

Type

Description

Default

module_id

int

Id of the source module in with display the related list.

related_module_id

int

Id of the target module to which the related list points.

related_field_id

int

Only for a n-1 related list. Id of the entity field related to the source module.

null

tab_id

int

Id of the tab in which display the related list. If it is null, Uccello displays the related list in a new tab of the Detail View.

null

label

string

Related list's name. It can be translated if you add the translation in the module localization file.

type

string

Type of the related list.

n-1 or n-n.

method

string

Method to use for retrieving the related records.

By default, set getDependentList for a n-1 related list and getRelatedList for a n-n one.

Theses methods are defined in the RelatedlistTrait.

To use your own method please refer to the example below.

sequence

int

The display sequence for ordering related lists.

data

array

Useful to pass some settings params.

By default you can use the following structure:

[

'actions' => [

'add',

'select'

]

]

This parameter allows to configure the buttons you want to display in the related list.

select: The user can select a record if he has the retrieve capability on the related module.

add: The user can create a new related record if he has the create capability on the related module.

You can adapt the contains of actions to use 0, 1 or 2 buttons (e.g. [ 'actions' => [ 'add'] ]).

If you don't want to display action buttons, simply let actions empty.

null

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