Skip to content
Thomas A Ellsworth edited this page Dec 28, 2017 · 2 revisions

Mappable

Define mappings on the protected $maps variable like bellow. Use this extension in order to map your 1-1 relations (BelongsTo, HasOne, MorphOne and MorphTo) AND/OR simple column aliasing (eg. if you work with legacy DB with fields like FIELD_01 or somereallyBad_and_long_name - inspired by @treythomas123)

<?php namespace App;

use Sofa\Eloquence\Eloquence; // base trait
use Sofa\Eloquence\Mappable; // extension trait

class User extends \Eloquent {

    use Eloquence, Mappable;

    protected $maps = [
      // implicit relation mapping:
      'profile' => ['first_name', 'last_name'],

      // explicit relation mapping:
      'picture' => 'profile.picture_path',

      // simple alias
      'dev_friendly_name' => 'badlynamedcolumn',
    ];

    public function profile()
    {
      return $this->belongsTo(Profile::class); // *
    }
  • ::class is PHP5.5 constant, in PHP5.4 use full namespaced string instead.

You can also add mapped attributes to the array representation of your model, just like any accessor:

    protected $maps = [
      'picture' => 'profile.picture_path'
    ];

    protected $appends = ['picture'];

You can get, as well as set, mapped attributes:

$user->profile->first_name; // 'Jarek Tkaczyk'
$user->first_name = 'John Doe';

$user->profile->first_name; // 'John Doe'

// mapped models are saved automatically for you:
$user->save();

You can also query the mappings:

// simple alias
User::where('dev_friendly_name', 'some_value')->toSql();
// select * from users where badlynamedcolumn = 'some_value'

// relation mapping
User::where('first_name', 'Romain Lanz')->toSql(); // uses whereHas
// select * from users where (
//   select count(*) from profiles
//    where users.profile_id = profiles.id and first_name = 'Romain Lanz'
// ) >= 1
 
// Order by related field
User::orderBy('first_name')->toSql(); // uses joins
// select users.* from users
//   left join profiles on users.profile_id = profiles.id
//   order by profiles.first_name asc
  
User::latest('users.created_at')->pluck('first_name'); // uses joins
// 'Romain Lanz'

Note that MorphTo mapping doesn't support join-based querying (orderBy, pluck and aggregates).

Explicit vs. Implicit mappings

Mappable offers 2 ways of defining mappings for your convenience.

Let's compare equivalent mappings:

// Assuming User belongsTo Profile
// and Profile hasOne Picture
// profiles table: id, first_name, last_name
// pictures table: id, profile_id, path


// User model
// explicit
protected $maps = [
  'first_name'   => 'profile.first_name',   // $user->first_name
  'last_name'    => 'profile.last_name',    // $user->last_name
  'picture_path' => 'profile.picture.path', // $user->picture_path
];

// implicit
protected $maps = [
  'profile'         => ['first_name', 'last_name'], // $user->first_name / ->last_name
  'profile.picture' => ['path'],                    // $user->path
];

As you can notice, behaviour is just the same. However, there is slight difference - explicit mapping offers more flexibility, in that you can define custom key for mapped value (picture_path), while with implicit mapping you have to use real attribute name defined in the related model (path).

Mappings work also with form model binding.

Important: Mind that each mapping call requires the relation to be loaded, so you may need to use eager loading in order to avoid n+1 query issue. However if you query mapped attributes, the relation is automatically eager loaded for you.

Clone this wiki locally