Laravel where on multiple collections at once - php

I'd like to ask whether is possible to use where method on multiple collections at once.. better to say, is possible to symplify this code somehow, please? Thank you so much.
$posts_per_weeks = array();
for($i = 10; $i > 0; $i--) {
$my_posts = $user->posts()
->where('created_at', '>', Carbon::now()->subWeek($i))
->where('created_at', '<=', Carbon::now()->subWeek($i-1))
->count();
$all_posts = Post::all()
->where('created_at', '>', Carbon::now()->subWeek($i))
->where('created_at', '<=', Carbon::now()->subWeek($i-1))
->count();
array_push($posts_per_weeks, [$my_posts, $all_posts - $my_posts]);
}
Method posts() looks like this:
public function posts()
{
if($this->hasAnyRole('broker|super-agent')) {
$posts = $this->teams()->get()->map(function ($team) {
return $team->posts->all();
});
if($this->hasRole('broker')) {
$posts->push($this->hasMany('App\Post', 'broker_id')->get());
}
return $posts->collapse()->unique();
} elseif($this->hasRole('admin')) {
return Post::all();
} else {
return $this->hasMany('App\Post', 'agent_id')->get();
}
}

You can use groupByto group the results
$maxWeeksBack = 10;
$myPostCount = $user->posts()->where('created_at', '>', Carbon::now()->subWeek($maxWeeksBack))
->select('id', DB::raw('count(*) as total'))
->groupBy(DB::raw('WEEK(created_at)'))
->get();
$postCount = Post::where('created_at', '>', Carbon::now()->subWeek($maxWeeksBack))
->select('id', DB::raw('count(*) as total'))
->groupBy(DB::raw('WEEK(created_at)'))
->get();
This returns the count of posts and myposts as an array, sorted by week. You would need to merge the two arrays to have your array, but this is a much cleaner approach to get the count of the posts.
DISCLAMER: Haven't tested it live, but it should work

You could use QueryScopes.
Post.php
class Post
{
public function scopeFromWeek($query, $week) {
return $builder->where('created_at', '>', Carbon::now()->subWeek($week))
->where('created_at', '<=', Carbon::now()->subWeek($week-1));
}
}
In your controller (or wherever you use this code)
$posts_per_weeks = [];
for($i = 10; $i > 0; $i--) {
$my_posts = $user->posts()->fromWeek($i)->count();
$all_posts = Post::fromWeek($i)->count();
array_push($posts_per_weeks, [$my_posts, $all_posts - $my_posts]);
}

Related

How to use limit and offset after get() in Laravel

I want to get all of the records in the first connection to DB and then get the number of those records.
$journeyItems = JourneyItem::whereIn('as_journey_id', $journeyIds->toArray())
->whereHas('content', function (Builder $query) {
$query->whereIn('as_content_type_id', [6, 7]);
})->get();
$totalRecords = $journeyItems->count(); // count=15
$journeyItems = $journeyItems->offset($start)
->limit($limit);
dd(journeyItems); // Error
Illuminate\Database\Eloquent\Collection::offset does not exist As a
result: It didnt work
How can i edit ?
I guess the method below is not the correct method
$journeyItems = JourneyItem::whereIn('as_journey_id', $journeyIds->toArray())
->whereHas('content',function(Builder $query) {
$query->whereIn('as_content_type_id', [ 6, 7]);
})
->offset($start)
->limit($limit)
->get();
$totalRecords = JourneyItem::whereIn('as_journey_id', $journeyIds->toArray())
->whereHas('content',function(Builder $query) {
$query->whereIn('as_content_type_id', [ 6, 7]);
})
->count();
$journeyItems is a collection, so use skip and take. offset and limit are query builder methods.
$journeyItems = $journeyItems->skip($start)->take($limit);
Alternatively, you can use slice:
$journeyItems = $journeyItems->slice($start, $limit);

Laravel where - how to reference same modal with conditions?

I'd like to use some custom logic inside a where() condition (Laravel 5.8), e.g. the below. The where() parameter will change depending on a variable. The code below doesn't work, but gives you an idea of what I'm trying to achieve. How can I get the desired result?
\App\Model::where(function ($query) use ($quantity, $price_criteria) {
if ($model->threshold_1 <= $quantity) {
$compare = $model->price_1
} elseif ($model->threshold_2 <= $quantity) {
$compare = $model->price_2
} else {
$compare = $model->price_3
}
$query->where($compare, "<=", $price_criteria)
}->orWhere...
If I understand your question correctly, you can use a whereRaw() query to build your conditions, or build separate query conditions with where()/whereOr().
Using whereRaw(),
\App\Model::whereRaw("price_1 <= ? AND threshold_1 <= ?", [$price_criteria, $quantity])
->orWhereRaw("price_2 <= ? AND threshold_2 <= ?", [$price_criteria, $quantity])
->orWhereRaw("price_3 <= ?", [$price_criteria]);
Or using Eloquent,
\App\Model::where(function($query) use ($quantity, $price_criteria) {
$query->where("price_1", "<=", $price_criteria)
->where("threshold_1", "<=", $quantity);
})->orWhere(function($query) use ($quantity, $price_criteria) {
$query->where("price_2", "<=", $price_criteria)
->where("threshold_2", "<=", $quantity);
})->orWhere(function($query) use ($quantity, $price_criteria) {
$query->where("price_3", "<=", $price_criteria)
});
Simply, you can't. All you do here is building a SQL query that returns data and you can't access the data before fetching it. In order to actually filter on data this way you will need to retrieve all data first and filter the collection.
You should pass $model as parameter like $quantity and $price_criteria (and add some punctuation :)):
\App\Model::where(function ($query) use ($quantity, $price_criteria, $model) {
if ($model->threshold_1 <= $quantity) {
$compare = $model->price_1;
} elseif ($model->threshold_2 <= $quantity) {
$compare = $model->price_2;
} else {
$compare = $model->price_3;
}
$query->where($compare, "<=", $price_criteria);
})->orWhere();
Hope that helps.

laravel whereMonth not working in collection

I want to initialize a collection that I will get from a table but if I use whereMonth on that collection it says that whereMonth does not exist.
I've used whereHas instead of initializing a collection but this becomes a very long code that could still possibly be reduced to a more efficient one.
$makati = [];
$cebu = [];
$davao = [];
for($x = 1; $x <= 12; $x++){
$makati[$x-1] = student::with('branch', 'program')
->whereHas('branch', function($query){
$query->where('name', '!=', 'Language Only');
})
->whereHas('branch', function($query) {
$query->where('name', 'Makati');
})->whereMonth('date_of_signup', $x)->whereYear('date_of_signup', '2019')->count();
}
This is working perfectly fine, but see that I will do the same code for the arrays $cebu and $davao.
$student = student::with('branch', 'program')->whereYear('date_of_signup', '2019')->get();
$student = $student->where('program.name', '!=', 'Language Only');
$makati = [];
$cebu = [];
$davao = [];
for($x = 1; $x <= 12; $x++){
$makati[$x-1] = $student->whereMonth('date_of_signup', $x);
info($makati);
}
I've tried this one but this is where the whereMonth error occurs.
the first code actually works but I want to make a shorter and efficient code.
$student = student::with('branch', 'program')->whereYear('date_of_signup', '2019');
$student = $student->where('program.name', '!=', 'Language Only');
$makati = [];
$cebu = [];
$davao = [];
for($x = 1; $x <= 12; $x++){
$makati[$x-1] = $student->whereMonth('date_of_signup', $x)->get();
info($makati);
}
Am thinking the whereMonth() method doesn't work on a gotten collection instance but on a query builder instance, so getting the collection makes the method inaccessible. like the code I have copied above, do not get until you are done using all the query builders. I hope this helps.
whereMonth('x', 1) is a shortcut that generates the SQL WHERE MONTH(x)=1 . SQL queries aren't generally mapped to the collection even though there is effort made to match collection methods to query builder methods as much as possible. The workaround is:
$student = student::with('branch', 'program')->whereYear('date_of_signup', '2019')->get();
$student = $student->where('program.name', '!=', 'Language Only');
$makati = [];
$cebu = [];
$davao = [];
for($x = 1; $x <= 12; $x++){
$makati[$x-1] = $student->filter(function ($value) use ($x) {
return $value->date_of_signup->month == $x;
});
info($makati);
}
This assumes that date_of_signup is properly casted to a date when retrieved using the $dates property in the model.
This is done in your student model:
class student extends Model {
protected $casts = [ 'date_of_signup' => 'date' ];
// rest of model
}
As a sidenote, it's probably more efficient if you do:
$student = student::with('branch', 'program')
->whereYear('date_of_signup', '2019')
->whereHas('program' => function ($query) {
$query->where('name', '!=', 'Language Only');
})
->get();
which will filter the results using an SQL query instead of getting everything and then filtering on the collection

Eloquent: Constraining an eager load relation where relation property is 0 or relation doesn't exist

I would like to get all the Report models where the relation ReportUpload's property of status equals 0 or where the ReportUpload relation doesn't exist. The Report and ReportUpload models have a one to one relationship, ReportUpload belongs to a Report.
Somewhat unsure how to go about this using eloquent's relationship constraints or any other method. Any help would be appreciated.
Here's my current code:
// initial query
$reports = Report::whereHas('link', function($query) {
$query->where('status', 'complete');
})->with('student', 'course', 'institution', 'reportUpload');
// apply constraint
if ($request->has('uploadStatus')) {
$uploadStatus = $request->has('uploadStatus'); // 0 or 1
if ($uploadStatus === 0) {
$reports = $reports
->whereDoesntHave('reportUpload')
->orWhereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
});
} else {
$reports = $reports->whereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
});
}
}
The code does not produce the desired results.
Edit
Trying this approach but not sure if it's correct:
$reports = $reports
->where(function ($query) use ($uploadStatus) {
$query
->whereDoesntHave('reportUpload')
->orWhereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
});
});
First, there are some mistakes in your initial code.
1 - You're checking if the request has an uploadStatus. Then, $uploadStatus == $request->has which will always be true.
if ($request->has('uploadStatus')) {
$uploadStatus = $request->has('uploadStatus');
So I guess you might want:
if ($request->has('uploadStatus')) {
$uploadStatus = $request->input('uploadStatus');
2 - You're comparing strictly $uploadStatus === 0 which might not work because the request might return a string '0' and not an integer, so you should either compare with == or cast $uploadStatus to (int).
After this, I think the code you added in your question works as expected:
$reports = $reports
->where(function ($query) use ($uploadStatus) {
$query
->whereDoesntHave('reportUpload')
->orWhereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
});
});
Because the where encapsulating the query will put it between parentheses.
Try to separate the queries. whereDoesntHave might be counting negatively with the orWhereHas even if it is an or statement:
$reportsNoUpload = $reports
->whereDoesntHave('reportUpload')->get();
$reportsIncomplete = $reports
->orWhereHas('reportUpload', function($query) use ($uploadStatus) {
$query->where('status', $uploadStatus);
})->get();
$reports = $reportsNoUpload->merge($reportsIncomplete);

How can I build a condition based query in Laravel?

I can do this in Code Igniter:
$this->db->select();
$this->from->('node');
if ($published == true)
{
$this->db->where('published', 'true');
}
if (isset($year))
{
$this->db->where('year >', $year);
}
$this->db->get();
How can this code be translated so that it works in Laravel?
In Fluent you can do:
$query = DB::table('node');
if ($published == true)
$query->where('published', '=', 1);
if (isset($year))
$query->where('year', '>', $year);
$result = $query->get();
As of Laravel 5.2.27, you can avoid breaking the chain by writing your conditions as so:
$query = DB::table('node')
->when($published, function ($q) use ($published) {
return $q->where('published', 1);
})
->when($year, function($q) use ($year) {
return $q->where('year', '>', $year);
})
->get();
To use Eloquent,just swap $query = DB::table('node') with Node:: but realize if both conditions fail, you'll get everything in the table back unless you check for some other condition before querying the db/model or from within the query itself.
Note the that $published and $year must be in local scope to be used by the closure.
You can make it more concise and readable by creating a macro. See: Conditionally adding instructions to Laravel's query builder
Here is how you can accomplish your query:
$year = 2012;
$published = true;
DB::table('node')
->where(function($query) use ($published, $year)
{
if ($published) {
$query->where('published', 'true');
}
if (!empty($year) && is_numeric($year)) {
$query->where('year', '>', $year);
}
})
->get( array('column1','column2') );
To find more information, I recommend reading through Fluent and Eloquent in the Laravel docs.
http://laravel.com/docs/database/fluent
I have not seen it here. You can even start your query like
$modelQuery = Model::query();
and then chain other query command afterwards. Maybe it will be helpful for someone new.
You can use Model::when() in Condition or you can create Builder::micro()
For Example
$results = Model::where('user_id', Auth::id())
->when($request->customer_id, function($query) use ($request){
return $query->where('customer_id', $request->customer_id);
})
->get();
If You need to create micro for a condition then. follow below instruction.
Write thic code in your serverice provider
Builder::macro('if', function ($condition, $column, $operator, $value) {
if ($condition) {
return $this->where($column, $operator, $value);
}
return $this;
});
Use Like Below Example
$results = Model::where('user_id', Auth::id())
->if($request->customer_id, 'customer_id', '=', $request->customer_id)
->get();
Ref: themsaid
If you need to use Eloquent you can use it like, I'm not sure that whereNotNull is the best use but I couldn't find another method to return what we really want to be an empty query instance:
$query = Model::whereNotNull('someColumn');
if(x < y)
{
$query->where('column1', 'LIKE', '%'. $a .'%');
}else{
$query->where('column2', 'LIKE', '%'. $b .'%');
}
$results = $query->get();
This way any relationships still work, for example in your view you can still use
foreach($results as $result){
echo $result->someRelationship()->someValue;
}
There is a good amount of info on here http://daylerees.com/codebright/eloquent-queries about this sort of stuff.
In Laravel > 5.2 you can use when():
$results = DB::table('orders')
->where('branch_id', Auth::user()->branch_id)
->when($request->customer_id, function($query) use ($request){
return $query->where('customer_id', $request->customer_id);
})
->get();
Docs: https://laravel.com/api/5.8/Illuminate/Contracts/Container/Container.html#method_when
Blog post: https://themsaid.com/laravel-query-conditions-20160425/
for eloquent query i used following that executes only if where condition has value
->where(function($query) use ($value_id)
{
if ( ! is_null($value_id))
$query->where('vehicle_details.transport_type_id', $value_id);
})
We can write like this (More precise way):
$query = DB::table('node')->when($published, function ($q, $published) {
return $q->where('published', 1);
})->when($year, function($q, $year) {
return $q->where('year', '>', $year);
})->get()
Not mentioned in Laravel docs. Here is pull request.

Categories