I am not sure on the correct logic to use in the following situation. This situation will come up several times in my app and I would assume, it may be experienced by others as well.
In Yii I have a loadModel function that is returning a CActiveRecord.
The function is as follows:
$model=Product::model()->with('performance','subcategory','sponsor')->findByPk($id);
As you can see, I am eagerly calling 3 relationships. One of those relationships - performance -
is a HAS_MANY relationship and relates to user reviews of the product.
So for product x, there may be 100 reviews all with different dates and scores.
What I am attempting to do is:
Pull all performance data (so all 100 reviews)
Pull the most recent performance data score (as long as it was submitted within the last 120 days)
The confusion in logic is this.
Should I create a function in my model class that goes through $model->performance to get the most recent information (#2).
Should I create an entirely separate relation just for this refined piece of data.
This most recent review data will be needed for each product in the CListView and the ListView needs to be sortable by this data. So, it seems as though it needs to be directly attached to the product active record that is being passed in to the view.
From both a performance standpoint and logic standpoint, how should I handle this?
As an aside, here is the current code I was trying to use that is not functioning:
public function scopes()
{
return array(
'recentPerf'=>array(
'condition'=>'perf_date > ' . strtotime('-120 days', strtotime(new CDbExpression('NOW()'))),
'order'=>'perf_date DESC',
'limit'=>1,
)
);
}
Thank you in advance for any suggestions!
Uday's answer got the scope working - now how is the correct way to use the scope?
Should I pass this amount in with the current model?
i.e. can I attach this to the:
$model=Product::model()->with('performance','subcategory','sponsor')->findByPk($id);
?
How I tested it to make sure it worked was:
$maxPerformance = ProdPerformance::model()->recentPerf()->find();
and am then passing that variable to the view. This seems like a very 'unclean' way of handling this situation. Should it instead be passed with the original $model variable?
I am not sure but possibly following line has a catch
'condition'=>'perf_date > ' . strtotime('-120 days', strtotime(new CDbExpression('NOW()'))),
condition is the data that will be sent to mysql so the date string should be in MySQL format not in PHP, try this
'condition'=>'perf_date > CURRENT_DATE - INTERVAL 120 DAYS',
Related
How can I respect the mm table sorting for a custom query in the repository in TYPO3?
Sample Repository code is:
public function findByCategory($categoryUid) {
$query = $this->createQuery();
$query->matching( $query->contains('categories', $categoryUid));
return $query->execute();
}
The Code above will return the Data as requested, but not ordered or ordered as defined in $this->defaultOrderings.
When I add:
$query->setOrderings(['categories.sorting' => 'ASC'])
this will be ordered by sorting in my category-table (as expected) but I can't find a way to get the records sorted by the MM table.
Sure, I can solve this via a custom doctrine query or a custom statement via ->statement($sql), but with both, I lose the model functions and I want to preserve those.
I've already spent several hours digging through documentation and google it and can't find a solution. I can't imagine that I'm the only one with this problem and that there is no solution for typo3, but I just can't find it.
Any Ideas?
I have a MySQL table called purchases and a model called Purchase in Laravel that is related to it. My table has two columns, quantity and unit_cost. The total cost of a purchase is not a column in my table. Instead, it is defined by an accessor in Laravel that multiplies unit_cost by quantity and returns it as total_cost.
Now I am trying to define a trend metric in Laravel nova that is supposed to output the value of my daily sales in the selected range. When I try to use return $this->sumByDays($request, Purchase::class, 'total_cost') it gives me an error that says total_cost is not a column.
Fair enough! So, I tried to use Purchase::selectRaw(DB::RAW('unit_cost * quantity AS total_cost')) in the second argument, but it didn't work. I saw multiple posts, none of them could solve the problem of accessing a model accessor in Laravel Nova metrics. Any help is appreciated.
Unfortunately you can't use the built-in sumByDays helper function.
But good news! You can still implement it yourself within the calculate function. Just make sure your function returns a Laravel\Nova\Metrics\TrendResult.
Something like this:
$purchases = DB::table('purchases')
->select(DB::raw('quantity * unit_cost as total_cost'))
->groupBy(DB::raw('DATE(created_at) DESC'))
->get();
return new TrendResult()
->trend($purchases);
I don't have time to test this at the moment but it should get you started.
While trying to fix this problem, I checked through the sum by day or hours code and I realised that the column is an instance of Query Expression.
So that means you can still use the built in nova sum by days.
Make sure you import use Illuminate\Database\Query\Expression;
Just do this
$xpression = new Expression('quantity * unit_cost');
return $this->sumByDays($request, Purchase::class, $xpression);
It worked for me easily, I hope it does for others too.
The expression could be used in various ways in my case it was to perform a different calculation.
A few words before
I know that you can append variables to model arrays and json representations by using the protected $appends = ["your", "vars", "here"]; array. But imagine the following situation:
The situation
Our use case would be a fictional game or similiar:
Imagine that we have a User model that holds simple information about an (human) user, like the full name, address and so on.
Now, we also have a Faction model that represents the faction/origin/guild/... of this user.
The Faction model is eager-loaded when retrieving users, because the Faction name is wanted almost every time when displaying the user information.
A User also has DailyStatistics, which holds some information about their daily scores (simple points would be enough).
The Clue
Because I want to know the points of the a faction, which is the sum of the user points, I thought about appending a new variable totalPoints.
The getTotalPointsAttribute function would look like this:
function getTotalPointsAttribute(){
return $this->users->sum->getTotalPoints();
}
The problem
Everytime when we retrieve a user now, the eager-loaded faction would also want to calculate the totalPoints attribute. That means, that we have a lot of overhead per user.
The question
Is there a way to avoid situations like this? Can I "conditionally" append variables? Are properties calculated when they are hidden?
I tried to wrap the totalPoints variable in a simple function, instead of an accessor instead. The problem is, that Frontend-Frameworks like VueJS would need access to the totalPoints variable (or to an endpoint to retrieve that value, but this solution is the least favorable).
I met this problem as I wanted to Appends on the fly but don't want this to auto-appends on any other Controller/Models (The other way is produce 2 Models for the same Table, which is difficult to maintain).
Currently I'm maintaining a Laravel 5.4 (Since they refuse to upgrade PHP5.6 to PHP7)
For Laravel 5.4 and below
Just add a closure after completed the query builder get()
->each(function ($items) {
$items->append('TotalPoints');
);
Source of original solutions: laravel-how-to-ignore-an-accessor
$openOrders = Order::open()->has('contents')
->get(['id','date','tableName'])
->each(function ($items) {
$items->append('TotalPoints');
);
Your model still contains the
public function getTotalPointsAttribute()
{
return $this->users->sum->getTotalPoints();
}
Now you can remove/comment out the the appends in your models :
protected $appends = [
'TotalPoints',
];
Alternatively, if you're on Laravel 5.5 and above, you could use the collection magic like so:
$openOrders->each->setAppends(['TotalPoints']);
Laravel 5.5 and above now have a Laravel 5.6 #Appending At Run Time
I have two tables:
Cards
Notes
Each Card has multiple Notes. So there is a relation between them like this:
class Card extends Model {
public function notes ()
{
return $this->hasMany(Note::class);
}
}
Ok well, all fine.
Now I need to understand the concept of these two lines:
$card()->$notes()->first();
and
$card()->$notes->first();
What's the difference between them? As you see in the first one $note() is a function and in the second one $note isn't a function. How will they be translated in PHP?
The first one points out to the card table and the second one points out to the notes table, right? or what? Anyway I've stuck to understand the concept of tham.
I don't know about $ before the $notes in your code but if you trying to say something like this.
1- $card->notes()->first();
2- $card->notes->first();
In the code in line 1, first you have a $card model and then you wanted to access all notes() related to that $card, and because of adding () after notes you simply call query builder on notes, show you can perform any other database query function after that, something like where, orderBy, groupBy, ... and any other complicated query on database.
But in the second one you actually get access to a collection of notes related to that $card, we can say that you get all related notes from database and set it into laravel collections and you are no more able to perform database query on notes.
Note: because laravel collections have some methods like where(), groupBy(), whereIn(), sort(), ... you can use them on the second one, but in that case you perform those methods on collections and not database, you already get all results from database
NB: My title was quite difficult to word - If you think of a way to word it more concisely I would appreciate that.
I'm using CakePHP and I am making a basic forum system. My models are set out as follows:
ForumSection has many ForumCategories
ForumCategory has many ForumPosts, has one ForumSection
ForumPost has many ForumPosts, has one ForumCategory (NB: ForumPost can be a thread or a reply)
On the index page of the forum, I would like to display the number of posts and replies in each forum category. I can do this by implementing the Containable relationship (which I have) into my ForumSection model and then using the following statement:
$sections = $this->ForumSection->find('all', array(
'contain' => array(
'ForumCategory' => array(
'ForumPost' => array(
'conditions' => array('ForumPost.is_thread', '1')
)
)
)
));
Then, on my view, I can simply echo the count of ForumPosts.
This is, of course, suboptimal -- I could be potentially bringing back thousands upon thousands of rows of data and loading it into memory when in reality I could do a direct SQL along the lines of SELECT COUNT(*) FROM ForumPosts WHERE forum_category_id = x AND is_thread = 1 for count and avoid this. I could even use a Cake function to do this for me but the point stands that it's more efficient than loading the entire table into memory just to count them.
This would, though, require making a loop in my controller (or model potentially but I'd still need to loop the sections in the controller) and meddling with the returned data so as to insert counts into it.
The way I see it, I have two options:
I could, in the view, get the post count as I loop over the categories, e.g.
foreach ($categories as $category):
// get post count
echo post count
I am reluctant to do this since, to my knowledge, this seems to ill-advised on MVC projects and I'm certain there'll be a more optimal approach I have not considered.
Intercept the found data and insert counts before patching the data through to the view. This seems like the most true to MVC way of doing it but it still feels wrong.
My question, ultimately, is does Cake provide any way of including the count of a model's inner relationships without including the entire data set for said relationship? If not, is there an approach that I could take that would be more straightforward/follow conventions more effectively than my suggested two?