Useful extensions for the Eloquent ORM.
🚧 WIP currently available extensions: Mappable
and Formattable
. For the time being, package does not follow semver.
If you want to know more about new extensions you can check our Roadmap!
- Jarek Tkaczyk (SOFTonSOFA) [email protected]
- Romain Lanz (https://github.com/RomainLanz) [email protected]
- This package requires PHP 5.4+
-
Require the package in your
composer.json
:"require": { ... "sofa/eloquence": "~0.3@dev", ... },
-
Add
Eloquence
trait to the model - it's entry point for other extensions and is required for them to work. -
Add other traits, that you want to use.
Define mappings on the protected $maps
variable like bellow. Use this extension in order to map your 1-1 relations 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
use Sofa\Eloquence\Contracts\Mappable as MappableContract; // interface
class User extends \Eloquent implements MappableContract {
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);
}
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'
// remember to save related model in order to persist the changes:
$user->profile->save();
// or simply use push:
$user->push();
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
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.
Define format on the protected $formats
variable like bellow.
<?php namespace App;
use Sofa\Eloquence\Eloquence; // base trait
use Sofa\Eloquence\Formattable; // extension trait
class User extends \Eloquent {
use Eloquence, Formattable;
protected $formats = [
// internal or global functions
'first_name' => 'strtolower|ucwords',
'last_name' => ['strtolower', 'ucwords'],
// static methods
'slug' => 'Illuminate\Support\Str::slug',
// instance methods
'short_name' => 'clip',
// passing additional parameters
'substring' => 'substr:0,5',
];
public function clip($string)
{
return substr($string, 0, 12) . '...';
}
}
$user->first_name = 'john';
$user->first_name; // 'John'
$user->last_name = 'doe';
$user->last_name; // 'Doe'
$user->slug = 'Awesome package!';
$user->slug; // 'awesome-package'
$user->short_name = 'I\'m really too long for this attribute!';
$user->short_name; // 'I\'m really t...'
$user->substring = 'shorten me to 5 letters';
$user->substring; // 'short'
Feel free to mix the extensions, however mind that the order of including traits matters.
<?php namespace App;
use Sofa\Eloquence\Eloquence; // base trait
use Sofa\Eloquence\Mappable; // extension trait
use Sofa\Eloquence\Formattable; // extension trait
use Sofa\Eloquence\Contracts\Mappable as MappableContract; // interface
class User extends \Eloquent implements MappableContract {
use Eloquence,
Mappable, Formattable; // order of these traits matters!
protected $maps = [
'picture' => 'profile.picture_path',
];
protected $formats = [
'picture' => 'strtolower',
];
public function profile()
{
return $this->belongsTo(Profile::class);
}
// ...
}
$user = User::first();
$user->picture; // some/path/to/file.jpg
$user->picture = 'Path/To/Another/file.JPG'; // value formatted then mapped
$user->profile->picture_path; // path/to/another/file.jpg
- Set relations on an array. (e.g.
protected $relations = ['profile' => 'has_one'];
)
...and much more to come soon!