setAppends on demand in Laravel - php

I have a model that has a bunch of attributes attached to it. The problem that I am having is that I do not need all the attributes for index method in my controller I need just a few. I have tried to attach them with setAppends method but it does not seem to work.
Here is what I tried:
$orders = new Order;
$order->setAppends(['overdueBy'])-get();
This simply returns the orders but doesn't have attributed attached. If I call it as static method which I seen while looking for the answer on the internet it throws an exception, I need to paginate results as well as apply some filters like orderBy but if I do so then it calls the method on Builder class that doesn't have it. Calling it on each model retrieved will create n+1 problem.
Is it possible to have appends on demand?

The setAppends method is for a single row. So you can use it as follows:
$order = Order::first(); // Or whatever you need
return $order->setAppends(['overdueBy]);
It's also possible to iterate over your collection and set it per record
$orders = Order::whichYouNeed();
$orders->each(function($order) {
$order->setAppends(['overdueBy']);
});
And do whatever you want.

If you want to append data at runtime then you should use
$order->setAppends(['overdueBy'])->toArray();
But remember this will override existing properties of the collection
for reference see Eloquent: Serialization
I would suggest you should use makeHidden method for hiding unnecessary
attributes reference see hiding attributes

Related

Laravel/Php - Modify the collection in eager loading closure?

I'm trying to modify this query:
The function of the helper class returns me the products left by removing the amounts used in sales, etc. It works fine if I use dd() inside the function. However, the $products collection isn't changed, what am I missing to successfully modify the base $products array with the collection returned by the helper?
If it helps, here is the helper class:
If there is a better way to handle this, I am glad for every suggestion.
Best regards
Edit:
I've tried the answer in the comment to pass the helper as closure parameter but still no luck.
I think you have to add a return inside the with orders function
return Helper::calculateNumberOfQuantityLeftPerOrder($query->get());

Laravel Eloquent assigning to a model attribute

Id like to overwrite a relationship attribute like the following:
$article['tags'] = $article->tags->pluck('title');
return compact('article');
It fails because it returns the tags attribute as an array of objects instead of an array of title strings. The API consumer needs the data in that form. This also reduces the size of the payload.
My current work around is to add another attribute and unset the original one.
$article['tagged'] = $article->tags->pluck('title');
unset($article['tags']);
return compact('article');
But I wish it was simpler and straightforward.
Use the relation method tags() to get the title array.
$article['tags'] = $article->tags()->pluck('title');
For overriding use setRelation(key, value):
$article->setRelation('tags', $article->tags()->pluck('title'));
^ This assumes you didn't preload the relationship. If you did, just remove the parenthesis from ->tags() (same as in your question: ->tags->pluck('title')).
For unsetting a single relationship, there's no available method. You could use ->setRelations(array) though.

pagination and factory in laravel4

Consider this code taken from here.
public function getIndex()
{
$posts = Post::orderBy('id','desc')->paginate(10);
// For Laravel 4.2 use getFactory() instead of getEnvironment() method.
$posts->getEnvironment()->setViewName('pagination::simple');
$this->layout->title = 'Home Page | Laravel 4 Blog';
$this->layout->main = View::make('home')->nest('content','index',compact('posts'));
}
As I understand it, pagination limits the number of rows, so I think paginate(10) means select first ten rows in the database. But I absolutely don't understand this.
// For Laravel 4.2 use getFactory() instead of getEnvironment() method.
$posts->getEnvironment()->setViewName('pagination::simple');
or
$posts->getFactory()->setViewName('pagination::simple');
And everything below. Mainly I don't understand what factory means and how it relates to pagination. I went to the laravel docs on Illuminate\Pagination\Factory and Illuminate\View\View but I can't find the meaning of factory. Can anyone explain the code above?
You are essentially setting how the pagination is output in HTML by selecting a specific paginator view, this allows you to have more than one type in an application or use different to the default.
Using multiple pagination types in the same application
Sometimes, you may want to use different pagination types across your
application. By default, Laravel will use the type specified in your
app/config/view.php file, so you need to override this setting when
you wish to use another type. Here is how to do so.
// This code should be in a controller or a route Closure.
// Let’s use the good old example of a list of blog posts.
$articles = Article::paginate(5);
Paginator::setViewName('pagination::simple');
/*
Alternatively, you could also use this to achieve the same result:
$articles->getEnvironment()->setViewName('pagination::simple');
For those who would like to know what’s happening under the hood, here is a more
detailed explanation:
1. Calling paginate() on an Eloquent model or a query builder will return an
instance of \Illuminate\Pagination\Paginator
2. Then, we need to get the related \Illuminate\Pagination\Environment of this
paginator via the well-named getEnvironment() method.
3. Finally, we can specify the pagination type we need. The default value is
'pagination::slider'.
The pagination types that are available by default are located in the
vendor/laravel/framework/src/Illuminate/Pagination/views directory.
*/
Source: http://laravel-tricks.com/tricks/using-multiple-pagination-types-in-the-same-application

Grouping filters in Propel

I currently have a Propel based query that looks like this:
$product = ProductQuery::create()
->filterByLive(1)
->filterByApproved(1)
->findOneByFilename($filename);
I regularly use this query and instead of having to chain the two filters I wondered whether it's possible to create a new filter that encapsulates them? This means that if I were to add an additional filter in future then I could simply do it in this single method rather than having to go through the entire project adding the new filter.
For example:
$product = ProductQuery::create()
->filterByIsActive()
->findOneByFilename($filename);
Is this possible?
You can create needed method in your ProductQuery:
public function filterByIsActive()
{
return $this
->filterByLive(1)
->filterByApproved(1);
}
Propel generates this file only once and you can put any code you like inside this class - and it will not be overwritten.

Passing two arrays from controller in cakePHP

I'm trying my best to learn MVC and cakePHP and I had a question about passing arrays to the view. Currently, I have some basic code below.
class AwarenesscampaignsController extends AppController {
public function view($id = null) {
$this->Awarenesscampaign->id = $id;
$this->set('data', $this->Awarenesscampaign->read());
}
This is what I "think" is currently happening.
AwarenesscampaignsController is set up. The view paramater requests id and matches it up with the Model, Awarenesscampaign. This matches up with the database and returns an array which is set to the variable "$data", and then the view is loaded.
My first question: is my understanding accurate?
What I would like to do is with this is to be able to pass another array, from a different model. For instance, I would like to query the table Posts (Controller: PostsController/ Model: Post).
For instance, my first attempt was to do the following inside the function:
$this->Post->find('all');
But this yields the error:
Indirect modification of overloaded property AwarenesscampaignsController::$Post has no effect [APP/Controller/AwarenesscampaignsController.php, line 20]
Additionally, I'm not sure how I would send both variables to the view.
To recap:
Was my understanding accurate?
How do I query a variable from another controller/model?
How do I sent this array to the appropriate view for that controller?
Thanks,
-M
You're on the right lines, and aren't doing it wrong per se. I would say your understanding is pretty good for a beginner.
By default Cake automatically loads a model that it thinks is directly related to the controller. So in AwarenesscampaignController, you can automatically access Awarenesscampaign (the model).
It doesn't know about any other model, though. One way you might solve this is by adding the following property to your controller:
// This has to contain ALL models you intend to use in the controller
public $uses = array('Awarenesscampaign', 'Post');
This goes at the top of the class, before you start declaring the functions. It tells Cake that you want to use other models except the 'default' one, but you have to add that one to the array too, or you'll lose access to it.
You can also use loadModel inside your action, if it's a one-off. It's then accessed the same way as you would access a model normally:
public function view($id = null) {
$this->loadModel('Post');
$posts = $this->Post->find('all');
...
}
To send this to your view, you can call set again, but you might want to change data to something more readable, and to prevent confusion:
public function view($id = null) {
...
$this->set('campaign', $this->Awarenesscampaign->read());
$this->set('posts', $this->Post->find('all'));
}
They'll be accessible as $campaign and $post respectively.
One tweak I would make, though, is to not use 'read' unless you intend to edit something. You can use findByColumnName to get the same data. Since you're using just an id, you can call findById:
$campaign = $this->Awarenesscampaign->findById($id);
There's quite a lot of magic going on there. It just means you can search for a particular value in a more short-hand format.
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
Finally, while you can access other models (as demonstrated), you can't, or generally shouldn't, try and access one controller from another. If you have code that you want to use in more than one controller, but can't go in the model, you can create Components.
http://book.cakephp.org/2.0/en/controllers/components.html#creating-a-component
The manual is fairly comprehensive. While sometimes hard to navigate, it will often have an answer to most of your questions.
http://book.cakephp.org/2.0/en/
1) Your understanding is good enough. What this is doing is basically mapping a row of database table with object. So after setting the Model id $this->Awarenesscampaign->id = $id, now Model is pointing to the row of database table that has id equals to what has been passed to view action.
2) you can query another table by calling the methods of that particular Model. If your model is somehow associated with the current Model that you are in, you can use chaining to call that Model's action. e.g. if your in Posts controller and Post Model is associated with Comment Model t get the data you can chain through.
$comments = $this->Post->Comment->find();
If however your Model of interest is not associated with current Model, there are couple of ways to perform operations of other Model. A good option is to use Class Registry. Say for example you want to use Customer Model which is not related to your current Model. In your controller you will do
$customer= ClassRegistry::init("Customer");
$customers= $customer->find();
3) to set multiple variables for the view you can set them via compact function or using associated row.
$posts = $this->Post->find();
$comments = $this->Post->Comment->find();
$this->set(compact('posts', 'comments'));
// or
$this->set('posts' => $posts, 'comments' => $comments);

Categories