Laravel, Eloquent and Kyslik sorting joined tables? - laravel

I'm stuck again. This time with the sorting links for 3 joined tables.
I'm building my query like this:
...
$query= Interaction::where('interactions.user_id', $id)->where('interactions.active', '1');
$query->join('contacts','interactions.contact_id','=','contacts.id');
$query->join('products','interactions.product_id','=','products.id');
$contact_username = request('contact_username');
$contact_name = request('contact_name');
$contact_type = request('contact_type');
$product_name = request('product_name');
$platform = request('platform');
$dateMode = request('dateMode');
$date = request('date');
$completed_at = request('completed_at');
$notesMode = request('notesMode');
$notes = request('notes');
//settings up some filters based on the request:
if ($contact_username) $query->whereJsonContains('contacts.username', $contact_username);
if ($contact_name) $query->where('contacts.name', $contact_name);
if ($contact_type) $query->where('contacts.type', $contact_type);
if ($product_name) $query->where('products.name', $product_name);
if ($platform) $query->where('interactions.platform', $platform);
if ($notes && $notesMode=='+') $query->where('interactions.notes', 'like', "%".$notes."%");
if ($notes && $notesMode=='-') $query->where('interactions.notes', 'not like', "%".$notes."%");
if ($date && $dateMode) $query->whereDate('date', $dateMode, $date);
if ($completed_at && $dateMode) $query->whereDate('completed_at', $dateMode, $completed_at);
//and finally:
$interactions = $query->sortable()->paginate(20,['interactions.*','contacts.username','contacts.name','contacts.type','products.name']);
...
Then in my view I have:
<th>#sortablelink('contact.username','Username')</th>
<th>Platform</th>
<th>Type</th>
<th>#sortablelink('contact.name','Name')</th>
<th>#sortablelink('product.name','Product')</th>
<th>#sortablelink('date','Date')</th>
<th>#sortablelink('notes','Notes')</th>
The last 2 links work great and everything is sorted as it should be, because they are from the "Interactions" table, but the links that should sort by the columns from the joined "Products" and "Contacts" tables fail.
What is the right way to reference these columns in the #sortablelink when they are coming from the joined tables?
PS: I have two other views which are working fine when only the "Products" and "Contacts" tables are used and the sorting for each column in these tables works fine, so I know the Models are set up correctly.

The solution turned out to be quite simple, but I had to sleep on the problem and tackle it again in the morning. :)
Instead of joining the two other tables manually, I did it like this:
$query= Interaction::where('interactions.user_id', $id)->where('interactions.active', '1')->with(['contact','product']);
//the two JOIN rows that were here were dropped. Everything else stayed the same.
and I added the relations in my "Interaction" model like this:
public function product() { return $this->belongsTo(Product::class); }
public function contact() { return $this->belongsTo(Contact::class); }
I hope this will be helpful to somebody. I lost a lot of hours trying to figure it out last night.

Related

How to express Larvel subqueries to use within a whereIn

I'm sorry for asking somthing so simple, but I can't get the docs (https://laravel.com/docs/9.x/queries#subquery-where-clauses)
I'm writting something like a social network functionality, so I have my messages table and my users table, there's another pivot table where I store the users the user follows, and they work pretty well.
I want to represent the following SQL in Laravel's ORM
SELECT * FROM mensajes
WHERE user_id=1
OR user_id IN (SELECT seguido_id FROM seguidos WHERE user_id=1)
The idea is I'll get the user's posts, and also the posts from the users that the user follows.
My following solution works, but I feel it's quite dirty, and should be solved with a Subquery
// this relation returns the users the user is following, ans works correctly
$seguidos = auth()->user()->seguidos;
// I store in an array the ids of the followed users
$seg = [];
foreach ($seguidos as $s) {
array_push($seg, $s->id);
}
array_push($seg, auth()->user()->id);
// Then I retrieve all the messages from the users ids (including self user)
$this->mensajes = Mensaje::whereIn('user_id', $seg)
->orderBy('created_at', 'desc')
->get();
I'd like to change everything to use subqueries, but I don't get it
$this->mensajes = Mensaje::where('user_id', auth()->user()->id)
->orWhereIn('user_id', function($query) {
// ... what goes here?
// $query = auth()->user()->seguidos->select('id');
// ???? This doesn't work, of course
}
->orderBy('created_at', 'desc')
->get();
You can simply construct the raw query as you have done with the SQL.
->orWhereIn('user_id', function($query) {
$query->select('seguido_id')
->from('seguidos')
->where('seguidos.user_id', auth()->user()->id);
});
But normally sub queries have a relationship between the primary SQL query with the sub query, you don't have this here and instead of doing one query with a sub query, you can quite simply write it as two queries. If you are not calling this multiple times in a single request, one vs two queries is insignificant performance optimization in my opinion.
Mensaje::where('user_id', auth()->user()->id)
->orWhereIn(
'user_id',
Seguidos::where('user_id', auth()->user()->id)->pluck('seguido_id'),
);

How do I combine data from two tables and display them in a single column?

I have two views, repair and maintenance. both are a combination of three tables: repair, r_list, item; and maintenance, m_list, and item. I need to put the id of repair and the id of maintenance into a single column named ticket_id in a third table, tickets. So basically it should be like this:
three tables plus three tables into one table combined
All three tables must exist simultaneously, meaning any change in repair should affect tickets, and the same thing with maintenance. I'm using CodeIgniter, and this is my code:
Model:
public function get_tickets_view()
{
$this->db->select("
tickets.id AS id,
repair.repair_id AS ticket_id,
maintenance.maintenance_id AS ticket_id,
items.item_id AS item_id,
");
$this->db->from("tickets");
$this->db->join("items", "tickets.item_id = items.item_id", "left");
$this->db->join("repair", "repair.item_id = tickets.item_id", "left");
$this->db->join("maintenance", "maintenance.item_id = tickets.item_id", "left");
$query = $this->db->get();
return $query->result();
}
Controller
public function tickets()
{
$data['ticket'] = $this->sample_model->get_tickets_view();
$data['main_content'] = 'page_address';
$this->load->view('navbar', $data);
}
Despite the page displaying properly, it refuses to actually pull the items inside either of those two table views. So what am I doing wrong?
You can combine arrays with
<?php $animals = array( "monkey","fish","ape"); $humans = array("Jim","Bob", "Josh");
array_merge($animals,$humans);
?>
There are tons of array functions here:
https://www.w3schools.com/php/php_ref_array.asp

Laravel HasManyThrough CrossDatabaseRelation

strange question:
I have 3 Models
Order
with id as PK
Orderline
with id as PK and order_id as FK. brand and partnumber are two separatet colums
Article
with combined PK brand and partnumber
**which is on another database **
One Order hasMany Orderlines. Every Orderline hasOneArticle.
i had make a function within order:
public function articles()
{
$foreignKeys = [
'brand_id',
'article_number',
];
$localKeys = [
'brand',
'partnumber',
];
return $this->hasManyThrough('App\Models\Masterdata\Articles','App\Models\Oms\OrderLine',$foreignKeys,$localKeys,'id','id');
}
How can i retrieve all Attributes from articles through Order?
I tried something like this:
$order = Order::find($orderid)->articles();
dd($order);
//did not work
$order = Order::with('orderlines.articles')->where('id','=',$orderid)->get();
Do you have an idea for me?
You can configure more than one database in the database.php config, and specify the $connection name in the Model class.
For the most part, Eloquent doesn't do JOINs, it just does IN statements on the key values from the preceding query, and programmatically marries the results together after the fact. Partly to avoid the mess of keeping table aliases unique, and partly to offer this kind of support- that your relations don't need to live in the same database.
In other words, what you have there should work just fine. If there's a specific error getting thrown back though, please add it to your post.

Laravel / Eloquent: Is it possible to select all child model data without setting a parent?

I have various parent/child relationships, drilling down a few levels. What I want to know is if its possible to do something like this:
$student = Student::find(1);
$student->bursaries()->enrolments()->courses()->where('course','LIKE','%B%');
(With the end goal of selecting the course which is like '%B%'), or if I would have to instead use the DB Query builder with joins?
Models / Relationships
Student:
public function bursaries() {
return $this->hasMany('App\StudentBursary');
}
StudentBursary:
public function enrolments() {
return $this->hasMany('App\StudentBursaryEnrolment');
}
If what you want is to query all courses, from all enrollments, from all bursaries, from a students, then, unfortunately, you are one table too many from getting by with the Has Many Through relationship, because it supports only 3 tables.
Online, you'll find packages that you can import / or answers that you can follow to provide you more though of solutions, for example:
1) How to use Laravel's hasManyThrough across 4 tables
2) https://github.com/staudenmeir/eloquent-has-many-deep
Anyhow, bellow's something you can do to achieve that with Laravel alone:
// Eager loads bursaries, enrolments and courses, but, condition only courses.
$student = Student::with(['bursaries.enrolments.courses' => function($query) {
$query->where('course','LIKE','%B%');
}])->find(1);
$enrolments = collect();
foreach($student->bursaries as $bursary) {
$enrolments = $enrolments->merge($bursary->enrolments);
}
$courses = collect();
foreach ($enrolments as $enrolment) {
$courses = $courses->merge($enrolment->courses);
}
When you do $student->bursaries() instead of $student->bursaries, it returns a query builder instead of relationship map. So to go to enrolments() from bursaries() you need to do a bursaries()->get(). It should look like this.
$student->bursaries()->get()[0]->enrolments(), added the [0] because im using get(), you can use first() to avoid the [0]
$student->bursaries()->first()->enrolments()
But I'm not sure if it will suffice your requirement or not.

Laravel return eloquent result where it matches all ids not just a couple

I can't wrap my head around this for some reason even though it seems the answer is right in front of me.
I have 3 tables, rentals, amenitiesand amenities_rentals
Amenities are things like air conditioning, staff ect.
The amenities_rentals has rentail_id, amenities_id. This table is basically a reference table that connects rentals to amenities.
In a search a user can narrow down rentals by checking off certain amenities. As of now its giving me all the rentals that have at leas one of the checked off options. I need it to narrow down to only rentals that have ALL of the chosen amenities. This is what I have, this is not only not pulling the info correct but I know there is a better way to do it.
if($request->has('amenities'))
{
$rental_ids = AmenitiesRentals::select('rental_id')
->whereIn('amenities_id',$request->amenities)
->distinct()
->get();
$builder->whereIn('rentals.id', $rental_ids->toArray());
}
The rest of the builder works fine.
As Kamal Paliwal suggested, define a BelongsToMany relationship in your Rental model:
public function amenities() {
return $this->belongsToMany(Amenity::class, 'amenities_rentals', 'rental_id', 'amenities_id');
}
Then use whereHas():
$amenities = $request->amenities;
$rentals = Rental::whereHas('amenities', function ($query) use ($amenities) {
$query->whereIn('amenities.id', $amenities);
}, '=', count($amenities))->get();
You should create a many relationship between rentals and amenities like below:
public function amenities()
{
return $this->belongsToMany(\AmenityModel::class, 'amenities_rentals', 'rental_id', 'amenities_id');
}
Then while applying filter you can add it in this way:
$amenitiesIds = $request->amenities;
RentalsModel::with('amenities')->whereHas('amenities', function ($q) use ($amenitiesIds) {
$q->whereIn('amenities_id', $amenitiesIds);
})
Hope this might help you.
You should select rentals that has the count of amenities ids same as the size of amenities array from your request by grouping rentals by amenities count.
Here is an example:
$amenities_ids = $request->amenities;
$amenities_ids_count = count($amenities_ids);
$rental_ids = AmenitiesRentals::selectRaw("COUNT(amenities_id) as amenities_count, rentail_id")
->whereIn('amenities_id', $amenities_ids)
->groupBy('rentail_id')
->having('amenities_count', $amenities_ids_count)
->get();

Resources