Why application hungs when using 'count' keyword as localScope function name - php

I have class Reservation in php Laravel 5.
I create localScope query.
When i use special word count, which is aggregate for sum in scope function name, my application hang up.
When i change function name to something not like keyword like total, everything works fine.
Why application hangs when i use special keyword ? How does process works. Stackoverflow ?
class Reservation extends Model
{
public function scopecount($query){
return $query->count();
}
}
I return it as:
$count = Reservation::currentMonth()->count();
My function currentMonth:
public function scopecurrentMonth($query){
return $query->where('date_from','>=', Carbon::now()->startOfMonth())
->where('date_to','<=', Carbon::now()->endOfMonth());
}
So why it hungs?
When i change name count to total:
public function scopetotal($query){
return $query->count();
}
and
$count = Reservation::currentMonth()->total();
everything works fine.
So why it hungs ?

You don't need to add a scope for count(), besides, it's already taken, unless you want to rename count to total?
You can just add ->count() to your queries and it will return an int.

Related

Dynamic model filter in Laravel's Eloquent

I'm looking for a way to make a dynamic & global model filter in Laravel.
I'm imagining a function like the following in my User.php model:
public function filter() {
return ($someVariable === true);
}
Whenever I do a query using Eloquent's query builder, I only want users to show up in the collection when the filter above returns true. I would have thought a feature like that existed, but a quick look at the documentation suggests otherwise. Or did I miss it?
I believe what you're looking for is Query Scopes.
They are methods that may be defined in a global or local context, that mutate the current query for a given model.
https://laravel.com/docs/5.5/eloquent#query-scopes
For example:
Lets say I have a database table called "Teams" and it has a column on it called "Wins." If I wanted to retrieve all Teams that had a number of Wins between Aand B I could write the following Local scope method on the teams model:
public function scopeWinsBetween($query, int $min, int $max)
{
return $query->whereBetween('wins', $min, $max);
}
And it could be invoked as such:
$teams = Teams::winsBetween(50, 100)->get();
I think you could use Collection macro but you will need to suffix all your eloquent get(); to get()->userDynamicFilter();
Collection::macro('userDynamicFilter', function () {
//$expected = ...
return $this->filter(function ($value) use($expected) {
return $value == $expected;
});
});
Thanks. For now I've simply added a post filter option to the models using the following code:
// Apply a post filter on the model collection
$data = $data->filter(function($modelObject) {
return (method_exists($modelObject, 'postFilter')) ? $modelObject->postFilter($modelObject) : true;
});
in Illuminate/Database/Eloquent/Builder.php's get() function, after creating the collection. This allows me to add a function postFilter($model) into my model which returns either true or false.
Probably not the cleanest solution but a working one for now.

Laravel paginate() on custom static functions

I want to paginate a custom static function.
When using Eloquent goes like this People::paginate(5); and paginates the results.
I need to do the same for this static function People::getOwners();
Just do your query and pagination in the function, like this:
public function getOwners() {
return self::query()->paginate(5);
}
Depending on what is going on inside getOwners(), you could turn it into a query scope. On your People model, add the function:
public function scopeGetOwners($query) {
// $query->where(...);
// $query->orderBy(...);
// etc.
$return $query;
}
Now, getOwners() is treated like any other query scope modifier (e.g. where, orderBy, etc.):
People::getOwners()->paginate(5);

Sum of all the columns of a rows in Laravel

How can I find out the sum of certain columns of a row in MySQL using Laravel?
I know ->sum('something'); gives you the sum of a column. But what about a row?
Is there any method to do so in Laravel?
Currently I'm adding each column values manually and getting the sum.
There is no built-in way to do this, but you can write a function yourself. Well, actually, I did that already for you! ;)
You have two options. The boring one, a function that just returns a predefined sum:
public function getSum(){
return $this->value1 + $this->value2; // and so on
}
Or a generic function that you can place inside a BaseModel and use in every class and with every attributes you want:
public function getAttributeSum(){
$sum = 0;
foreach(func_get_args() as $attribute){
$sum += $this->getAttribute($attribute);
}
return $sum;
}
And you call it like this:
$model->getAttributeSum('value1', 'value2');
Just create a model function and pass all the variables to it and do the calculation there. Then return the total and print wherever you want it.
{{Classmodel::total($yourvariablearray)}}
In the Classmodel.php you will have something like:
public static function total($variablearray){
return $total = $variablearray->columnone + $variablearray->columntwo;
}
This should work.
How about this
return \DB::table('table_name')->sum('column_to_be_calculated');
Works for laravel 5 .

Laravel 4: How to apply a WHERE condition to all queries of an Eloquent class?

I'm trying to implement an "approved' state for a table I have, it's pretty straightforward, basically, if the row's approve column equals 1; that row should be retrieved, otherwise it shouldn't.
The problem is, now I have to go through the whole codebase and add a WHERE statement(i.e., function call) which is not only time consuming but also inefficient(if I ever want to remove that feature, etc.)
How can I do that? Is it as easy as adding $this->where(..) inside the Eloquent child class' constructor? Wouldn't that affect other CRUD operations? such as not updating an unapproved row?
The answer was given when there was no query scope feature available.
You can override the main query, only for the Post model, like
class Post extends Eloquent
{
protected static $_allowUnapprovedPosts = false;
public function newQuery()
{
$query = parent::newQuery();
if (!static::$_allowUnapprovedPosts) {
$query->where('approved', '=', 1);
} else {
static::$_allowUnapprovedPosts = false;
}
return $query;
}
// call this if you need unapproved posts as well
public static function allowUnapprovedPosts()
{
static::$_allowUnapprovedPosts = true;
return new static;
}
}
Now, simply use anything, but unapproved users won't appear in the result.
$approvedPosts = Post::where('title', 'like', '%Hello%');
Now, if you need to retrieve all posts even unapproved ones then you can use
$approvedPosts = Post::allowUnapprovedPosts()->where('title', 'like', '%Hello%');
Update (Using the query scope):
Since, Laravel now provides Global Query Scopes, leverage that instead of this hacky solution, notice the date of this answer, it's too old and so much things changed by now.
// Using a local query scope
class Post extends Eloquent
{
public function scopeApproved($query)
{
return $query->where('approved', 1);
}
}
You can use it like:
$approvedPosts = Post::approved()->get();
The closest thing I found is Eloquent query scope.
Even though it requires a minor change in my code(prefixing queries) it still gives me what I'm looking with great flexibility.
Here's an example:
Create a function within the Eloquent child class:
class Post extends Eloquent {
public function scopeApproved($query)
{
return $query->where('approved', '=', 1/*true*/);
}
}
Then simply use it like this:
$approvedPosts = Post::approved()-><whatever_queries_you_have_here>;
Works perfectly. No ugly repeated WHERE function calls. easy to modify. Much easier to read(approved() makes much more sense than where('approved', '=', 1) )
You can use global scope for your need, docs for that are here : https://laravel.com/docs/5.6/eloquent#query-scopes
Good example is SoftDeletingScope which is applied to all queries by default on models which use SoftDeletes trait.

Decrement function, decrement all the rows in the tables

Im using laravel v3.2.12-4, and I have an problem with the decrement function. Instead of update only one row, this method affects all the rows in the column. Im using Eloquent, and I have a many_to_many relationship.
The code that constains the decrement method is:
foreach ($ids as $id) {
$indicator = Indicator::find($id);
$tags = $indicator->tags()->get();
foreach ($tags as $tag) {
$indicator->tags()->detach($tag->id);
if ($tag->frequency == 1) {
$tag->delete();
} else {
// I have to made this code to fix the problem with decrement function
// But i want to use decrement
$tag->frequency = $tag->frequency - 1;
$tag->save();
// This dosnt work for me.
// $tag->decrement('frequency');
}
}
$indicator->delete();
}
In the model class Indicator i made the relation with this function:
public function tags()
{
return $this->has_many_and_belongs_to('Tag');
}
In the model class Tag i made the relation with this function:
public function indicators()
{
return $this->has_many_and_belongs_to('Indicator');
}
Well, if I made an update to the column this result OK for me, but when If I use the decrement function this affect all the rows and I don't know if this a bug or something with this method.
Thanks.
This is how it's designed. The decrement() method is actually defined on the Query Builder and not on Eloquents builder. What this means is that when you call $tag->decrement('frequency') it's actually falling through to the QB and simply running a query like UPDATE tag SET frequency=frequency - 1. Notice that there's no WHERE clause?
You could still use the decrement() method but you'd have to do it like this.
$tag->where_id($tag->id)->decrement('frequency');
Now you've set the WHERE clause and only that tag will be decremented. Of course, the cleaner solution is what you've got. Or perhaps this.
$tag->frequency--;
Not tested, may throw an error of some sort.

Categories