Assume A,B,C,D are models. Is there a way to cleanly chain models like so: a->b()->c()->d()->get();? When trying to do this, I get an error since a->b(), b->c(), and c->d() all return sets and not a single object.
Some people have suggested eager loading in other sites, but I have no idea how to use them. So far I have tried using a->load('b.c.d'); in hopes of loading all the models to 'a' but it didnt work.
How can I load all relevant models b,c,and d to a?
You can use with
$v = a->with('b.c.d')->get();
then you can use in your code like this
$v->b->c->d
Related
I have a laravel Model with a one to many relationship which the user can edit via a multiple select tag.
Before exporting the model as a JSON, I use the "pluck" method to get an array of related IDs instead of an array of models, so that they can be used in the select tag and later be synced again with the "sync" method of Laravel.
However the result of "pluck()" seemingly doesn't persist over serialization. The following code doesn't work -upon serialization, "relationship" becomes again an array of objects-
$model->relationship = $model->relationship->pluck('id');
This one, however, does what it should: somePropertyIHaveJustCameUpWith is an array of IDs
$model->somePropertyIHaveJustCameUpWith = $model->relationship->pluck('id');
1) Why does this happen?
2) I have seen there is this resources way in the documentation, but creating an entire new class for something that could be solved with a single line of code feels like a bit overkill. Isn't there a cleaner way to do that?
I think this is likely a result of the way the model implements toArray().
The you can trace the steps taken, but eventually the relations are read from the $this->relations property on the Model, not from each individual relationship.
So, instead of setting the value of your relation directly like:
$model->relationship = $newValue
... you could try setting it using:
$model->setRelation('relationship',$newValue)
This will update the $model->relations property.
This should allow the toArray() method to get the new value that you set when serializing.
Note that the toJson() method in turn calls the toArray() method when serializing. So either approach will be the same result.
I have a script that is passed a table name. I want to be able to load relations of relations for the records returned. For instance, I can get this by using the classname directly:
Item::find($id)->schedules->load('template')->toArray();
This gives me what I want, however, the table name passed is variable, plural and lowercase. To fix this I could just do something like:
$table_class = studly_case(str_singular($table));
$table_class::find($id)->schedules->load('template')->toArray();
However, this is a little clunky. I'd like to do it like so:
DB::table($table)->find($id)->schedules->load('template')->toArray();
But the above doesn't work and I haven't been able to figure out the correct syntax for it. How would I do this using DB::table()?
With DB::table() your are using the query builder. Eager loading is a feature of the eloquent orm. Afiak there is no way to do this via the query builder.
But imho your clunky solution is absolutly ok, I solved a similar problem the same way. If you are just worried about the code style, you could implement a simple ModelFactory that does this "ugly" job, something like this:
ModelFactory::create($table)->find($id)->...
Okay, so here's the deal. I'm working with a custom CMS, and I'd like for the code to be as optimized as possible. I've been reading/watching tuts/etc. like crazy about the repository pattern in general as well as specifically using it with Laravel's Eloquent. There are probably some really dumb questions/thoughts in here, but bear with me. :-) Sometimes there's no easy way to ask about terminology/best practices without looking silly.
As with many things, there are million ways I could "make it work"; my dilemma is essentially a matter of "best practice."
General Scenario/Question
Let's assume I am trying to get a Page for my CMS from the database. From what I can understand the typical way to set up the repository pattern with Eloquent is to have the following files:
Page.php -- the Eloquent Model
PageRepositoryInterface.php -- the "contract" for what should be in Page repo's
EloquentPageRepository.php -- the Page repository that can grab data via Eloquent
Easy enough. So I might use it this way. Assuming I have a getPageById method in EloquentPageRepository.php, I could just do something like this from my controller:
$page = $this->repo->getPageById();
Now my question arises: what type of data should getPageById() return? Some people recommend setting it up to return an Eloquent collection. Others say just a plain array or generic object.
Ideally I feel like my scenario would best lend itself to having EloquentPageRepository grab the data from Eloquent and actually return an instance of a custom Page class that I have. For example, something along the lines of this:
<?php namespace Acme\Repositories\EloquentPageRepository;
use Acme\...\PageObject as PageObject; // Better name than PageObject?
//...
class EloquentPageRepository implements PageRepositoryInterface {
// Omitting constructor, etc.
public function getPageById($id)
{
// Grab the row via Eloquent (obviously not stored in Page:: at
// this point. I'm just using it here for clarity and time's sake.
$page = Page::find($id);
// Now we have an Eloquent collection stored in $page, but I'd
// like to return the data inside an instance of my custom class.
$pageObj = new PageObject($page->title, $page->body);
return $pageObj;
}
}
To me, this seems good because it gives a consistent delivery format from repo to repo. It also allows me to perform some constructor logic on my pageObject. Finally, it allows me to have some custom methods on the PageObject (that are repository-agnostic).
It's similar to a collection, but I don't think it's exactly that. It's basically just an instance of a class that I'm immediately populating with my database info.
My questions, listed:
Is it considered bad practice to use a repo to stuff eloquent data into a specific object and return it?
I don't want to call my class "PageObject," because that's just lame. I'd way rather call it something like "PageCollection," except for the fact that it's not actually a collection. Is there actually a name for the way that I'm using this class? It's not a collection, it's a ...? I have no idea about this, I'm just searching for any input you have.
It whole depends on what you expect from the repository pattern. Are you using the repository pattern because in the future you're going to swith of data layer and needs a new repository. If you're using Eloquent as long as your cms live then you can return an eloquent object. If you want it very flexible then make a new page object(PageComposer as mentioned in the comments). This is one of the strengts of the repository pattern so I suggest you make a PageComposer class which you instantiate and return by the repository.
Normally you can call it Page because its a page and it ships some information of a page. But that name you've already give to the Eloquent model. You can consider changing the eloquent models name and call your return object Page.
Ok, so I'm trying to figure out eager loading to improve application performance. I'm working on a service database, but am having some problems. Here is my code
$service_data = Client::join('service_requests','clients.id','=','service_requests.client_id')
->join('service_request_comments','service_requests.id','=','service_request_comments.service_request_id')
->whereNotNull('service_request_comments.time')->with('service_requests')->with('service_requests.comments')->get();
The query does properly pull the data, but it loads ALL service_requests and service_requests.comments into the first client_object, instead of loading a client object with the service_requests objects and then loading the service_request objects with the appropriate comments.
Each of these relationships are one to many...
...A client can have many service_requests
...A services_request can have many comments
...and by extention...
...A client can have many comments
I tried using the constraints, but that just gave me a couple dozen 500s with various syntax. Could somebody tell me what I'm doing incorrectly?
Don't use joins if you're doing eager loading. You're essentially combining Query Builder commands with Eloquent features, and the two are conflicting. Something like this should get the same thing (skipping the where constraint for now):
$service_data = Client::with('service_requests.comments')->get();
Note that the nested with() request will automatically eager load service_requests and then their comments.
If you need the query on the comments relationship, there are a couple ways to do so, and I don't want to make assumptions about exactly what you're using for (or if it's necessary at all), so I'll skip that for now.
This is the example from the cookbook:
class IngredientsController extends AppController {
public function index() {
//grab all ingredients and pass it to the view:
$ingredients = $this->Ingredient->find('all');
$this->set('ingredients', $ingredients);
}
So it looks like we're using find('all') and returning all of them to the index. I figure we can also create a method called frenchIngredients() that would return all the french ingredients.
My question is do I have to create a separate function for every find condition I want to have for the Controller?
I'm really new to cakePHP, so maybe I just don't understand how this is supposed to work, but the way I'm seeing it, I'm going to have to create large numbers of functions and/or views just to account for changes in find conditions.
Does cakePHP suck that bad or am I missing something? I thought it would make my PHP OOP easier, but really it just seems like a lot of overhead to convert something working into something I have no idea how to work.
Cake has a lot of "magic" functions. You can do findBy<columName> or even findAllBy<columName>. You can also chain the column names like findBy<colum1>And<colum2>.
I suggest you read their cook book article on models. The cook book is a really great way to start and have information about what the framework can do for you. If you want more information on a function you can always go peek in their api which is more complete.
In any frameworks such as ZF and Codeigniter , I think this is actually the process of building your model.
In your example in CakePHP, if you created it with Bakery then you already have this functionality. There are lots of database functions which is included in every model but your job is to extend it. Not all things is already made in your model.
The only thing I can suggest is to be sure and make sure not to be redundant on building Model methods. If you think "freshIngredients" will be the same with other models you can implement a base Model and extend it to your new models. Like creating a Base model with method of "newItems" which will return newly added items or in your part it's ingredients.
There is happily no need to create a function for each simple conditional find.
The find function takes more than one argument (see the doc in Retrieving Your Data) and one of its parameters are the find conditions.
Do you want to fetch only the french ingredients ? Let's say you have categories for the ingredients, and that the 'french ingredient' category has the ID 2, you would do it like this:
$this->Ingredient->find('all', array('conditions' => array('category_id' => 2)));
And conditions passed to find can be much more complex than this simple example.
But off course, it often makes sense to create functions in models when you have some more complex logic than a simple find query.