Laravel | Return rows in order based off pivot - laravel

I have the following setup:
stages:
| id | name | order |
commands:
| id | name | body |
------------------------------=
| 1 | test | phpunit |
| 2 | style | echo "style" |
| 3 | deploy | deploy |
command_stage:
| command_id | stage_id | order |
---------------------------------
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 1 | 2 |
Basically, I would like to create a method on the stage model which allows me to get all of the commands back based off of the order, but commands have a specific order and so do the stages.
So each command is stored under a stage so we know which part to run but each command also has an order in that stage. Now I know I can do something like the following:
$inOrder = collect();
Stage::get()->each(function ($stage) {
$commands = $stage->commands()->orderByPivot('order')->get();
$inOrder->push($commands);
});
return $inOrder;
But I was wondering if there is a nicer way to do this? Or even a way to do this solely on one database hit?

Pre-load and sort the relationship:
$stages = Stage::with(['commands' => function ($subQuery) {
$subQuery->orderBy('command_stage', 'order');
}])->get();
foreach($stages as $stage) {
$inOrder->push($stage->commands);
}
This method of Eager-Loading will reduce the N+1 query issue of doing $stage->commands() inside the foreach() loop.

Related

Unique counts in a callback

I have a list of users, and I'm trying to get the result of each unique domain in users' email addresses and their totals.
So, let's say I have these 5 users:
+--------------------------------------+--------------------------------+-------------+------------+-------------+
| id | email | firstname | lastname | something |
+--------------------------------------+--------------------------------+-------------+------------+-------------+
| 00c0f0db-87d0-45b2-8ed2-aa94d1e3e659 | shane.conte#jourrapide.com | Shane | Conte | iew9anap0L |
+--------------------------------------+--------------------------------+-------------+------------+-------------+
| 0114360a-3ef8-49d6-8c51-02392bc51e10 | michelle.guitierrez#dayrep.com | Michelle | Guitierrez | eeNgiev3foh |
+--------------------------------------+--------------------------------+-------------+------------+-------------+
| 00e8e2f2-2130-4f65-8914-b93d5b029d75 | terri.hebert#rhyta.com | Terri | Hebert | vahMoKiuCh0 |
+--------------------------------------+--------------------------------+-------------+------------+-------------+
| 00e1578b-cf6d-46b8-92e3-2388a80105f7 | richard.copeland#dayrep.com | Richard | Copeland | Iem4mohng |
+--------------------------------------+--------------------------------+-------------+------------+-------------+
| 00f1be34-d60e-4b2f-b3ae-610c67151f2d | elsie.fuhrman#rhyta.com | Elsie | Fuhrman | aPie6piD6ae |
+--------------------------------------+--------------------------------+-------------+------------+-------------+
After running the query, I'd like to see this result:
+-------+----------------+
| count | domain |
+-------+----------------+
| 1 | jourrapide.com |
+-------+----------------+
| 2 | dayrep.com |
+-------+----------------+
| 2 | rhyta.com |
+-------+----------------+
I'm currently running this query below to get the unique domains but if I try to run count() in it, it dramatically fails after 300 seconds, which I expect to happen since I have A LOT more than 5 users :)
r.db('helloworld').table('users').pluck('email').map(function(user) {
return user('email').split('#').nth(1).downcase()
}).distinct().map(function(domain) {
return {
count: '???', // <--- this is where I need help
domain: domain
}
})
And as you can imagine, it perfectly returns this result:
+-------+----------------+
| count | domain |
+-------+----------------+
| ??? | jourrapide.com |
+-------+----------------+
| ??? | dayrep.com |
+-------+----------------+
| ??? | rhyta.com |
+-------+----------------+
I hope this makes sense. If you think I'm on a wrong path, feel free to suggest any other way.
Thanks in advance!
You can't distinct() to count(). Instead you want to group():
r.db('helloworld').table('users').pluck('email').map(function(user) {
// wrap the result in an object for grouping purposes
return { domain: user('email').split('#').nth(1).downcase() };
})
// this groups all domains together in a [{ group, reduction }] list of objects
.group('domain')
// after group(), calls are scoped to each reduction: count each one
.count()
// let's ungroup to scope the following calls to the whole sequence
.ungroup()
// let's be compliant with the format you expect
.map(function(doc) {
return {
domain: doc('group'),
count: doc('reduction')
};
});

Use a non-unique route key name?

We have rows which are sequenced and scoped to each organisation. So:
Processes tbl:
| id | org_id | sequence | name |
|---- |-------- |---------- |------------------- |
| 23 | 1 | 1 | New person |
| 56 | 1 | 2 | Person leaves |
| 84 | 1 | 3 | Person absent |
| 26 | 2 | 1 | Something happens |
| 34 | 2 | 2 | Something else |
| 77 | 2 | 3 | And another |
We do this so they can have a simple numbering system for their own records rather than a UUID, hash or something else. We prefer to have this in the url as well, so:
example.com/processes/1
Once logged in, we know their org_id, so for each query we can use org_id along with the sequence from the url.
Is there a clean laravel-esque kind of way to achieve this, maybe using the Model's getRouteKeyName?
I got the answer with some help from the laracasts forum:
/**
* Retrieve the model for a bound value.
*
* #param mixed $value
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value)
{
return $this->where('sequence', $value)
->where('org_id', /* however you get your org_id */)
->first() ?? abort(404);
}

Laravel: Query the models where the relationship is greater than 0

I'm doing a Laravel query to query a list of elements that his relationship is grater than 0.
The table concerts:
| ID | NAME |
|----|-----------|
| 1 | Concert A |
| 2 | Concert B |
| 3 | Concert C |
And the Position table.
| ID | concert_id | user_id | Content |
|----|----------------|------------|----------|
| 1 | 1 | 1 | xxx |
| 2 | 1 | 2 | yyy |
| 3 | 3 | 1 | zzz |
| 4 | 3 | 2 | www |
| 5 | 1 | 3 | xyx |
| 6 | 3 | 3 | rer |
The query that I need to do is, get the concerts where their position has in the content a value like $some_query$.
The code I've is the following:
$result = $this->model->whereHas('positions')
->with(['positions' => function ($query) {
$query->where('content', 'like', "%{$content}%");
}])->get();
But as far as I can tell, this will bring all the concerts, and also this is going to bring only the positions that has the desired content.
So I've two problems, the first one is I need to get the concerts that his queried positions are greather than 0.
And the second one is that also I need to bring all the positions, not only the queried ones.
Basically the query is just a way to know which concerts I need to bring.
Is there possible to achieve this on a single query?
You'll have to move your query to the whereHas.
If I understood well, this is what you want:
$result = $this->model
// Take the Concerts that has positions with a similar content
->whereHas('positions', function ($query) use ($content) {
$query->where('content', 'like', "%{$content}%");
})
// Take (all) the Positions of these concerts
->with('positions')
->get();

Laravel: Proper Querying Many to Many Relationships

I have 2 tables and a third pivot table as follows:
Table 1: Files
id
title
Table 2: Pools
id
title
is_visible (1,0)
Table 3: file_pools
file_id
pool_id
All Models and Relationships are in place.
A File may belong to none, 1 or more pools
A Pool may have none,1 or more files
Now, i would like to create a scope visible in the Model File such that:
the query includes any File that belongs to none or 1,or more active Pools
should not include any File that has at least 1 invisible (is_visible=0) Pool, even if this File belongs to another visible Pool
Below is what I have tried but this includes files that are both in visible and invisible Pools
public function scopeVisible($query)
{
return $query->doesntHave('pools')->orWhereHas('pools', function ($query) {
$query->where('is_visible',1);
});
}
any help highly welcome
EDIT: Add Sample Data
table: files
| id | title |
|----------|---------------|
| 1 | File A |
| 2 | File B |
| 3 | File C |
| 4 | File D |
table: pools
| id | title | is_visible|
|----------|---------------|---------- |
| 1 | Pool 1 | 1 |
| 2 | Pool 2 | 0 |
table: file_pools
| file_id | pool_id |
|----------|---------------|
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
| 3 | 2 |
The Expected Result : (scope:visible)
| id | title |
|----------|---------------|
| 1 | File A |
| 4 | File D |
Hope this clarifies
If I understand your situation correctly:
public function scopeVisible($query)
{
return $query->whereDoesntHave('pools', function ($query) {
$query->where('is_visible', 0);
});
}

Laravel Eloquent GroupBy statement

I have this table and i'm trying to achieve a multiple groupBy and distinct through Laravel with a sum of duration. Basically, i need to find in table multiple ids passed from controller and then if windows, method and type are the same, sum the duration value.
Here is table structure
+-----+---------+--------+----------+------+
| ids | windows | method | duration | type |
+-----+---------+--------+----------+------+
| 1 | 2 | 3 | 5 | 2 |
+-----+---------+--------+----------+------+
| 2 | 2 | 3 | 5 | 2 |
+-----+---------+--------+----------+------+
| 3 | 2 | 3 | 5 | 2 |
+-----+---------+--------+----------+------+
and here is the statement that i try to use:
$tickets_g = \App\Models\Ticked::whereIn('id', $tickets)
->select('categoria', 'tipologia', 'priority', 'durata')
->distinct()
->GroupBy('categoria')
->GroupBy('tipologia')
->GroupBy('priority')
->sum('durata')
->get();
Can someone help me to figure out?
Fixed myself this is the correct query if someone stuck in the same issue:
$tickets_g = \App\Models\Ticked::select('support_ticked.*', DB::raw("SUM(support_ticked.durata) as durata_totale"))
->groupBy('support_ticked.tipologia','support_ticked.priority', 'support_ticked.categoria')
->get();

Resources