How to map Laravel/Eloquent results to custom class - php

I'm looking for a way to map the results of Laravel / Eloquent database queries to custom classes, rather than the default Eloquent class.
Does Laravel / Eloquent include any built-in facility for doing this? If not, is there a suitable place to 'hook' into the result generation code and do the necessary mapping?
As an example, this is roughly what I'd like to achieve:
class User extends Eloquent {}
class MyUser
{
protected $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
}
$users = User::all();
// $users should now contain an array of MyUser instances
Motivation / Reason for question
The motivation behind this question is to find a way in which queries can produce objects or arrays of objects that are completely framework-independent. This is because the Laravel app in question needs to be able to pass its results to other non-Laravel systems, so hence Plain Old PHP Objects (such as MyUser) make the most sense.

Laravel will not give you something like that, but you can do with PHP. Inject your Eloquent User class into your custom class, Laravel will inject it for you automatically. Use the object inside your class as you wish and, if you need to call one or another Eloquent method, you can just provide fallbacks to the Eloquent User object.
A good option is to use the repository pattern, where your class will expect to receive an implementation of a repository interface, for that you have to:
Create the interface for your user repository, all repositories, including your Eloquent model, must implement this interface. This is a contract to let you switch the implementation of the repository whenever you want, without having to touch your class. It also will make your class framework agnostic.
interface UserRepositoryInterface {
}
Your implementations of this repository could be:
class EloquentUser extends Eloquent implements UserRepositoryInterface {
}
class DoctrineUser extends DoctrineWhatever implements UserRepositoryInterface {
}
Create your class
class User extends Eloquent {}
class MyUser
{
protected $name;
public function __construct(UserRepositoryInterface $user)
{
$this->userRepository = $user;
}
public function __call($name, $arguments)
{
return call_user_func_array(array($this->userRepository,$name), $arguments);
}
public static function __callStatic($name, $arguments)
{
return call_user_func_array(array('User',$name), $arguments);
}
public function getName() {
return $this->userRepository->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
}
Now in Laravel to select the implementation you just have to
App::bind('UserRepositoryInterface', 'EloquentUser');
To switch to doctrine, you just have to
App::bind('UserRepositoryInterface', 'DoctrineUser');
And if you need to use this class outside Laravel, you just have to instantiate it, passing whatever implementation of the repository you want:
$user = new MyUser(new DoctrineUser);
No more ties to Laravel.

for Eloquent models, you might want to take advantage of ->toArray() method then typecast it using (object) to get POPO.
e.g.
$user = (object) User::find(1)->toArray();
print_r($user);
you should get:
stdClass Object
(
[id] => 1
[name] => John Doe
[email] => fakeuser0#mail.com
[active] => 1
...
[created_at] => 2017-10-26 17:45:53
[updated_at] => 2017-10-26 17:45:53
[deleted_at] =>
)

I also wanted to have custom class for this pre-defined class "User".
class User extends Model implements UserInterface, RemindableInterface {
to UserModel class:
class UserModel extends Model implements UserInterface, RemindableInterface {
So rather than hacking through the codes and classes, actually there is config for it.
Open app/config/auth.php
Make sure you change:
'model' => 'UserModel', // change to UserModel or your custom named class
'table' => 'User', // change if your table is non default "User"
It should be good to go.
:)

Related

Is there a way to decouple Laravel Eloquent models from Service or Controller level?

There is a simple Laravel Eloquent Model below:
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
}
and it's normal to use repository pattern to work with model, like:
use Product;
class ProductRepository implement ProductRepositoryInterface
{
public function __construct(Product $model)
{
$this->model = $model;
}
public function findById($id)
{
return $this->model->find($id);
}
...
}
The controller use the repository to get Prodcut data:
class ProductController extends Controller
{
private $productRepository;
public function __construct(ProductRepository $productRepository)
{
$this->productRepository = $productRepository;
}
public function getSomeInfoOfProduct($id)
{
$product = $this->productRepository->findById($id);
return [
'name' => $product->name,
'alias' => $product->alias,
'amount' => $product->amount,
];
}
}
In the method getSomeInfoOfProduct, when I am deciding what kind of information should I return, I don't know there are how many properties the $product object has until I look at the schema of table products or migration files.
It's look like that the controller is tightly coupled with Eloquent models and the database. If one day, I store the raw data of products in Redis or other places, I still need to create a Eloquent model object, and fill in the object with the data from Redis.
So I am considering to create a pure data object to replace the Eloquent Model object, like below:
class ProductDataObject
{
private $name;
private $alias;
private $amount;
private $anyOtherElse;
public function getName() {
return $this->name;
}
....
}
and let the repository return this object:
use Product;
use ProductDataObject;
class ProductRepository implement ProductRepositoryInterface
{
public function __construct(Product $model)
{
$this->model = $model;
}
public function findById($id)
{
$result = $this->model->find($id);
// use some way to fill properties of the object
return new ProductDataObject(...);
}
...
}
In the controller or service level, I can just look at ProductDataObject to get all information I need. And it also looks like easier to change data storage without affecting the controllers and services.
Does this way make sense?
I think what you're looking for is the Factory Pattern. You're kind of on the right track already. Basically you have a middle-man class that your Controller or Repository basically asks to supply them with the appropriate Model. Through either parsing conditions or a config file using .envs, it figures out which one to serve up, so long as anything it returns all implements the same Interface.

Traits with PHP and Laravel

I am using Laravel 5.1 and would like to access an array on the Model from the Trait when the Model before the model uses the appends array.
I would like to add certain items to the appends array if it exists from my trait. I don't want to edit the model in order to achieve this. Are traits actually usable in this scenario or should I use inheritance?
array_push($this->appends, 'saucedByCurrentUser');
Here is how my current setup works.
Trait
<?php namespace App;
trait AwesomeSauceTrait {
/**
* Collection of the sauce on this record
*/
public function awesomeSauced()
{
return $this->morphMany('App\AwesomeSauce', 'sauceable')->latest();
}
public function getSaucedByCurrentUserAttribute()
{
if(\Auth::guest()){
return false;
}
$i = $this->awesomeSauced()->whereUserId(\Auth::user()->id)->count();
if ($i > 0){
return true;
}
return false;
}
}
Model
<?php namespace App;
use App\AwesomeSauceTrait;
use Illuminate\Database\Eloquent\Model;
class FairlyBlandModel extends Model {
use AwesomeSauceTrait;
protected $appends = array('age','saucedByCurrentUser');
}
What I would like to do is something to achieve the same effect as extending a class. I have a few similar traits, so using inheritance gets somewhat ugly.
trait AwesomeSauceTrait {
function __construct() {
parent::__construct();
array_push($this->appends, 'saucedByCurrentUser');
}
}
I have seen some workarounds for this, but none of them seem better/cleaner than just adding the item to the array manually. Any ideas are appreciated.
Update
I discovered this way of accomplishing what I need for one trait, but it only works for one trait and I don't see an advantage of using this over inheritance.
trait
protected $awesomeSauceAppends = ['sauced_by_current_user'];
protected function getArrayableAppends()
{
array_merge($this->appends, $this->awesomeSauceAppends);
parent::getArrayableAppends();
}
How I am currently handling my Model, for what it is worth.
model
public function __construct()
{
array_merge($this->appends, $this->awesomeSauceAppends);
}
Traits are sometimes described as "compiler-assisted copy-and-paste"; the result of using a Trait can always be written out as a valid class in its own right. There is therefore no notion of parent in a Trait, because once the Trait has been applied, its methods are indistinguishable from those defined in the class itself, or imported from other Traits at the same time.
Similarly, as the PHP docs say:
If two Traits insert a method with the same name, a fatal error is produced, if the conflict is not explicitly resolved.
As such, they are not very suitable for situations where you want to mix in multiple variants of the same piece of behaviour, because there is no way for base functionality and mixed in functionality to talk to each other in a generic way.
In my understanding the problem you're actually trying to solve is this:
add custom Accessors and Mutators to an Eloquent model class
add additional items to the protected $appends array matching these methods
One approach would be to continue to use Traits, and use Reflection to dynamically discover which methods have been added. However, beware that Reflection has a reputation for being rather slow.
To do this, we first implement a constructor with a loop which we can hook into just by naming a method in a particular way. This can be placed into a Trait of its own (alternatively, you could sub-class the Eloquent Model class with your own enhanced version):
trait AppendingGlue {
public function __construct() {
// parent refers not to the class being mixed into, but its parent
parent::__construct();
// Find and execute all methods beginning 'extraConstruct'
$mirror = new ReflectionClass($this);
foreach ( $mirror->getMethods() as $method ) {
if ( strpos($method->getName(), 'extraConstruct') === 0 ) {
$method->invoke($this);
}
}
}
}
Then any number of Traits implementing differently named extraConstruct methods:
trait AwesomeSauce {
public function extraConstructAwesomeSauce() {
$this->appends[] = 'awesome_sauce';
}
public function doAwesomeSauceStuff() {
}
}
trait ChocolateSprinkles {
public function extraConstructChocolateSprinkles() {
$this->appends[] = 'chocolate_sprinkles';
}
public function doChocolateSprinklesStuff() {
}
}
Finally, we mix in all the traits into a plain model, and check the result:
class BaseModel {
protected $appends = array('base');
public function __construct() {
echo "Base constructor run OK.\n";
}
public function getAppends() {
return $this->appends;
}
}
class DecoratedModel extends BaseModel {
use AppendingGlue, AwesomeSauce, ChocolateSprinkles;
}
$dm = new DecoratedModel;
print_r($dm->getAppends());
We can set the initial content of $appends inside the decorated model itself, and it will replace the BaseModel definition, but not interrupt the other Traits:
class ReDecoratedModel extends BaseModel {
use AppendingGlue, AwesomeSauce, ChocolateSprinkles;
protected $appends = ['switched_base'];
}
However, if you over-ride the constructor at the same time as mixing in the AppendingGlue, you do need to do a bit of extra work, as discussed in this previous answer. It's similar to calling parent::__construct in an inheritance situation, but you have to alias the trait's constructor in order to access it:
class ReConstructedModel extends BaseModel {
use AppendingGlue { __construct as private appendingGlueConstructor; }
use AwesomeSauce, ChocolateSprinkles;
public function __construct() {
// Call the mixed-in constructor explicitly, like you would the parent
// Note that it will call the real parent as well, as though it was a grand-parent
$this->appendingGlueConstructor();
echo "New constructor executed!\n";
}
}
This can be avoided by inheriting from a class which either exists instead of the AppendingGlue trait, or already uses it:
class GluedModel extends BaseModel {
use AppendingGlue;
}
class ReConstructedGluedModel extends GluedModel {
use AwesomeSauce, ChocolateSprinkles;
public function __construct() {
// Standard call to the parent constructor
parent::__construct();
echo "New constructor executed!\n";
}
}
Here's a live demo of all of that put together.
I thought I'd add an update for 2019 since this was one of the first discussions that popped up when trying to do a similar thing. I'm using Laravel 5.7 and nowadays Laravel will do the reflection that IMSoP mentioned.
After the trait has been booted, Laravel will then call initializeTraitName() on the constructed object (where TraitName is the full name of the trait).
To add extra items to $appends from a trait, you could simply do this...
trait AwesomeSauceTrait {
public function initializeAwesomeSauceTrait()
{
$this->appends[] = 'sauced_by_current_user';
}
public function getSaucedByCurrentUserAttribute()
{
return 'whatever';
}
}
KISS:
I don't see any reason why you should use trait when your are simply appending attributes.
I would only recommend using trait without a constructor like you were doing, only if you model is getting pretty bulky and you wish to slim down things.
Please also note this not the correct way of appending attribute
protected $appends = array('age','saucedByCurrentUser');
You could do this:
protected $appends = array('age','sauced_by_current_user');
Appends attribute names should the snake_case of its method Name
Edited:
The idea behind appends is to dynamically add fields that doesn't exist in your database table to your model so after you can do like:
$model = FairlyBlandModel ::find(1);
dd($model->sauced_by_current_user);

Eloquent models - How do i break this dependancy

Take the following:
<?php
class EqUserRepository implements IUserRepository
{
//...
public function GetUserByID( $id )
{
return User::find( $id );
}
}
You can see that it returns a User
User is extending Model (Which extends Eloquent)
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
public function Profile()
{
return $this->hasOne( "Profile" );
}
Now imagine in our controller, we inject our concrete implementation via the interface:
class UserController extends Controller
{
private $userRepository;
public function __construct( \App\Models\Contracts\Repositories\IUserRepository $userRepo )
{
$this->userRepository = $userRepo;
All good so far!
Now we have a controller method that calls upon our injected repository and gets a User returned...
public function GetDetails( $id )
{
$user = $this->userRepository->GetUserByID( $id );
return view( 'user', array( "Model" => $user ) );
}
Now in our view we call upon the profile:
<p> Name: <?= $Model->Profile()->first_name ?> </p>
Oops, now our view is dependant on Eloquent because the Profile method on User is making calls using Eloquent.
I have been trying to figure out the best solution to break this dependancy.
I thought that having my repository map data into a User class that did not extend Eloquent was a good idea... but then you have some looping to map the classes which is a bit of a waste of time... especially considering you could just as easily use mysql_fetch_object( $query, "User" ) and have it map straight away.
So taking 1 into account, i thought the DB class must have a way of mapping direct to a type...
Have the User depend upon my IUserRepository, then my Profile function could look like this and that would certainly break the dependency, in a really good way!
.
public function Profile()
{
return $this->userRepository->GetProfileByUserID( $this->id );
}
But you ofcourse cannot DI into an eloquent model. I tried using the boot overriding method, but that doesn't like DI too, of course...
So, question time:
Can you set the return type of the DB class?
Can you somehow DI on an eloquent model?

Laravel 4 setting up model using the IoC container

I recently watched this video and wanted to change my Laravel controllers so that they had their dependencies managed with Laravel's IoC container. The video talks about creating an interface for a Model and then implementing that interface for the specific data source used.
My question is: when implementing the interface with a class that extends Eloquent and binding that class to the controller so that it is accessible from $this->model, should I also create interfaces and implementations for the Eloquent models which may be returned when calling methods such as $this->model->find($id)? Should there be different classes for the Model and the ModelRepository?
Put it another way: how do I do new Model when my model is in $this->model.
Generally, yes, people doing that pattern (the repository pattern) have an interface which have some methods defined that your app will use:
interface SomethingInterface {
public function find($id);
public function all();
public function paged($offset, $limit);
}
Then you create an implementation of this. If you're using Eloquent, then you can make an Eloquent implementation
use Illuminate\Database\Model;
class EloquentSomething {
protected $something;
public function __construct(Model $something)
{
$this->something = $something;
}
public function find($id)
{
return $this->something->find($id);
}
public function all() { ... }
public function paged($offset, $limit) { ... }
}
Then you make a service provider to put it all together, and add it into app/config/app.php.
use Something; // Eloquent Model
use Namespace\Path\To\EloquentSomething;
use Illuminate\Support\ServiceProvider;
class RepoServiceProvider extends ServiceProvider {
public function register()
{
$app = $this->app;
$app->bind('Namespace/Path/To/SomethingInterface', function()
{
return new EloquentSomething( new Something );
});
}
}
Finally, your controller can use that interface as a type hint:
use Namespace/Path/To/SomethingInterface;
class SomethingController extends BaseController {
protected $something;
public function __construct(SomethingInterface $something)
{
$this->something = $something;
}
public function home() { return $this->something->paged(0, 10); }
}
That should be it. Apologies on any errors, this isn't tested, but is something I do a lot.
Downsides:
More code :D
Upsides:
Able to switch out implementations (instead of EloquentSomething, can use ArraySomething, MongoSomething, whatever), without changing your controller code or any code that uses an implementation of your interface.
Testable - you can mock your Eloquent class and test the repository, or mock your constructor dependency and test your controller
Re-usable - you can App::make() to get the concrete EloquentSomething anywhere in your app and re-use the Something repository anywhere in your code
Repository is a good place to add additional logic, like a layer of cacheing, or even validation rules. Stock mucking about in your controllers.
Finally:, since I likely typed all that out and STILL DIDN'T ANSWER YOUR QUESTION (wtf?!), you can get a new instance of the model using $this->model. Here's an example for creating a new Something:
// Interface:
public function create(array $data);
// EloquentSomething:
public function create(array $data)
{
$something = this->something->newInstance();
// Continue on with creation logic
}
Key is this method, newInstance().
I've used $newModel = $this->model and it's worked for me.

How can I use Yii components while keeping my service layer abstracted?

I like and use the Yii framework, particularly its "components", which are lazily-instantiated and you can swap them in or out in your configuration file. Kind of like a dependency injection-lite.
I try to keep the business logic of my code completely independent of the Framework, in case I ever want to repurpose that code, or even change frameworks.
Let's say I have a class in my service layer called AccountService, which implements IAccountService and has a one-argument constructor.
interface IAccountService
{
function getUserById($id);
}
class AccountService implements IAccountService
{
private $_userRepository;
public function __construct(IUserRepository $userRepository) {
$this->_userRepository = $userRepository;
}
public function getUserById($id) {
return $this->_userRepository->getById($id);
}
}
Great. So far, it's totally framework-free. Now I'd like to expose this as a Yii component, so it can be lazily-instantiated and easily used by Yii controllers and other Yii components.
But Yii components (which implement IApplicationComponent) must have exactly zero constructor arguments, while my class requires one!
Any ideas?
Here's what I've got. I'm not really happy with any of them; they both look over-engineered and I'm detecting a distinct smell from them.
Option 1 - compose: I create a class called "AccountServiceComponent" which implements Yii's IApplicationComponent. It cannot extend my AccountService class, because of the constructor, but it could instantiate one as a private member and wrap all of its methods, like so:
class AccountServiceComponent implements IApplicationComponent, IAccountservice
{
private $_accountService;
public __construct() {
$this->_accountService = new AccountService(new UserRepository());
}
public getUserById($id) {
return $this->_accountService->getUserById($id);
}
}
Cons: I'll have to wrap every method like that, which is tedious and could lead to "baklava code." Especially considering that there'll be multiple service classes, each with multiple methods.
Option 2 - mixin: (Or behavior or trait or whatever it's called these days.)
Yii (having been written prior to PHP 5.4) offers "behaviors" in the form of a class which implements IBehavior. I could create a behavior class which extends my service, and attach it to a component:
class AccountServicesBehavior extends AccountService implements IBehavior
{
// Implement the few required methods here
}
class AccountServiceComponent implements IApplicationComponent
{
public function __construct() {
$accountService = new AccountService(new UserRepository());
$this->attachBehavior($accountService);
}
Cons: My component no longer officially implements IAccountService. Also seems to be getting excessive with the layering.
Option 3 - optional constructor parameters:
I could just make the constructor parameter to my service class optional, and then extend it into a component:
class AccountService implements IAccountService
{
public $userRepository;
public function __construct(IUserRepository $userRepository = null) {
$this->userRepository = $userRepository;
}
public function getUserById($id) {
return $this->_userRepository->getById($id);
}
}
class AccountServiceComponent extends AccountService implements IApplicationComponent
{
}
Cons: The optional constructor parameter means this class coudld now be instantiated without supplying it with everything it needs.
...so, any other options I'm missing? Or am I just going to have to choose the one that disturbs me the least?
Option 3 but with an object as the optional argument sounds best imo:
public function __construct(IUserRepository $userRepository = new UserRepository()) {
$this->userRepository = $userRepository;
}

Categories