How can I add a model into the relations array of another model?
E.g.
Domain belongsTo Owner.
Owner hasOne Domain.
I have $domain (instance of Domain).
I have $owner (instance of Owner).
I want to add $domain to $owner->relations[] so that I can just use $owner->domain later on in my code.
The reason for doing this is such that in one particular controller i only need a partial data set from each model so use fluent to query with a join for performance reasons then fill the models.
Then for readability's sake I'd like to use $owner->domain->id etc
$domain->owner()->associate($owner); gives me a $domain->owner option
But then I can't work out the opposite version
$owner->domain()->associate($domain)
$owner->domain()->attach($domain)
both result in the following fatal error
Call to undefined method Illuminate\Database\Query\Builder::[attach|associate] ()
NB: I don't want to save anything as I've already loaded all the data i need.
setRelation() should work. It sets the value in the relations array.
$owner->setRelation('domain', $domain);
When setting a one to many relationship, you may need to use values():
$owner->setRelation('domains', $domains->values());
Related
I'm using Laravel 5.4. I have two related eloquent models:
ImageFile and ClipartImage.
ClipartImage contains a belongsTo relationship as follows:
class ClipartImage extends Model
{
public function image_file()
{
return $this->belongsTo(ImageFile::class, 'image_file_id');
}
public function promote()
{
$this->image_file->promote();
}
}
On ClipartImage there is a "promote" method which will pass the promote call through to the ImageFile object as it knows how to promote itself.
The problem I'm running into is if properties change on ImageFile as a result of the method call, there are a couple of properties on the related ClipartImage which may need to be updated as a result. In the ImageFile class I could search the database for any related ClipartImages and update the relevant fields, but this doesn't update the existing ClipartImage instance that I am working with. What's the normal way to solve this sort of dependency between models and keeping the object instances in memory in sync?
I guess I'm in need of some sort of Observer pattern but in Laravel these things seem to be handled at the static/class level not for individual object instances - ie I could create an observer class that looked for any changes to ImageFile models, but that isn't going to help me refresh or update my existing ClipartImage object.
Edit: I've used a single "promote" method to illustrate, but there's a bunch of interaction between ClipartImage and the ImageFile class and it's not obvious from the point of view of ClipartImage when it may need to refresh or update itself. Ideally ImageFile would inform it when key fields have changed so it can respond accordingly.
When working with repository pattern we have interface and some repository classes which implement this interface. If I'm not mistaken, one of the SOLID principles says that those repository classes should return the same type of data for each interface method so that if we switch implementation of the interface nothing breaks down.
If I have an eloquent repository class, which returns an array of eloquent classes of all users, like return User::all(), I have not a simple array but an array of Eloquent models. So instead I might want to use toArray() to return simple array, so that if I switch my implementation of the interface to some other ORM (for example UserDoctrineRepository or I don't know...) everything will still work.
If I understand correctly, inside UserEloquentRepository we use an eloquent model class to help us get data using Eloquent. However, inside my model (User class) I might have some helper methods, like getFullName(). If I simple use toArray() inside UserEloquentRepository I won't get this helper method in my controller, and, eventually in my view.
In other articles I've read they keep it like return User::all(), however, it means that I'm still coupled to Eloquent as I don't get a simple array, but an eloquent objects array
What you get from User::all() or basically every Eloquent query is a Illuminate\Database\Eloquent\Collection. The problem is that when you call toArray() on the collection it will convert all the items of the collection into an array too and you loose all the methods of your model. Instead you can call all() on the collection to get to the array of model objects:
$collection = User::all();
return $collection->all();
Yes that still means you will have Eloquent specific models in your resultset however if you don't use Eloquent features like attribute accessors you will have an easy time replacing it with another type of model.
I have a model that I require to update, such as User. In a large application, this User will also have relationships, and updating it would be more than a mere User::find($id)->update(Input::all()).
Should the updating be performed in the Controller, or be a method within the User model itself?
I always thought that I should put it in the model, since it is model specific. But I've seen most people perform the task in the controller. What is the reasoning for this?
As said in the pattern names, the controllers control any model query or update. Based on this, the controller will be responsible for fetching, changing and saving the model.
If some actions are to be reused in many controllers, acting on a set of models, they would go in a "repository".
In your example, you are using Input::all() to hydrate your model, which you definitely have to change. Any user value has to be checked and/or sanitized. I would also recommend against using all and prefer either get or post, so you set up a minimal restriction.
So, I definitely let the controller be responsible for the "glue", having repositories for any collection matter, services and hydrators to change the object, and the model itself staying for only getters and setters, managing their own data in their own scope.
Suppose I have these models - Post, Category, User. Post belongsTo Category and User.
In my PostsController inside some action I want to get data from categories or users table I can use one of the following
1) public $uses = array('Post', 'Category', 'User') and use Category model like
$this->Category->find('all'); everywhere in that controller
2) $this->Post->Category ->find('all');
3) $this->loadModel('Category '), than use
$this->Category->find('all') in that action
So, my question is which one of these will have the best performance and why ? Maybe for small applications it won't have any visible effect, but what if the project is big and there are dozens of models that should be used in the same controller and every little performance improvement does matter.
Thanks
If you already have the association in the model, use that (#2). This makes sense because these models would most likely already be loaded (if you're not caching).
Otherwise, use the loadModel function. This way you'll be loading the model only when you need it. Either way, unless you have some major work going on in your constructors (you shouldn't), it really won't matter that much.
I have actually 3 questions, but similar to each other:
I have a model called Permissions. and I need another model ie. Users. what is the proper way to user Permissions inside Users.
You get what I mean above, is that weird or is there another better way to do it.
This Permissions model, will be used globally throughout my application, what is the way to use it in other models or controllers (similar with 1st question)
One way is to get the global variable for the main CodeIgniter object, and then load the model from that object rather than $this:
class Permissions extends Model
{
function MyPermissionsFunction()
{
$ci =& get_instance();
$ci->load->model('users');
$ci->users->MyUserFunction();
}
}
You can also sidestep the issue by combining your models together into one larger model. The main reason to keep them separate is to only load the models that you need; if you nearly always use both together (as would make sense, for Users and Permissions), you may be better off taking this approach.