I have no idea how to solve my problem.
I have Model1 with relation:
class Model1 extends BaseModel
{
public function details(): BelongsToMany
{
return $this->belongsToMany(
Detail::class,
'model1_details',
'model1_id',
'detail_id',
);
}
}
model1_details is my pivot table.
Also I have array of details ids like [1, 2, 3].
I need to fetch Model1 that belongs to ALL Detail with given ids.
I need to do this inside my filter.
That's what I have inside my controller:
$builder = Model1::filter(new ModelListFilter($request));
and inside ModelListFilter:
protected function filter(): Builder
{
$request = $this->request;
$request->whenFilled('details', function ($query) {
//
});
return $this->builder;
}
I've tried:
$request->whenFilled('details', function ($query) {
foreach($query as $detailId) {
$this->builder->whereHas('details', function (Builder $innerQuery) use ($detailId) {
$innerQuery->where('detail_id', $detailId);
});
}
});
But it returns all models Model1 even without any details.
UPD
So the problem wasn't there =) 'details' just wasn't filled in my Request.
also my $query was a string, not array, so I called json_decode on it.
Code above retrieves Models belonging to detail with id=1 AND to detail with id=2 and so on.
But I think there might be better solution so I'll leave this question open
UPD 2
also changed this
$innerQuery->where('detail_id', $detailId);
to this
$innerQuery->where('id', $detailId);
so here is needed to pass columns we have in 'details' table, not columns from pivot table
I can't see where you load the relationship with details. Not sure if you're missing a file, but in case you're not, load the relationship in the first part of the query with the eager loading with method:
$builder = Model1::with('details')->filter(new ModelListFilter($request));
You are almost there but you still missing two things,
WhereIn list
to get model whereHas their details ids in a list you need to use WhereIn instead of looping the query
Eager load the details relationship
to get the details list with each model you have to use the with keyword to eager load the relationship
Solution
// builder
$builder = Model1::with('details')->filter(new ModelListFilter($request));
// filter
$request->whenFilled('details', function (array $details) {
$this->builder->whereHas('details', function (Builder
$innerQuery) use ($details) {
$innerQuery->whereIn('detail_id', $details);
}
});
Found solution to my problem here. So it seems like there is no better solution(
Related
To keep things simple, I'll give an example in code to what I'm trying to Achieve.
Let's assume that we have a model called User with the following code:
class User
{
public function posts()
{
return $this->hasOne(Post::class);
}
}
And the Post model look something like this.
class Post
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
And now what I want to be able to do is for example:
$user = Auth::user();
$comments = $user->post->comments; //here, I only want the "comment_id" and "comment_content" fields to be included, not all the Comment Model fields !
you can simply use hasManyThrough in user model to connect comments table.
read about hasManyThrough here: docs
and after having a relation between user and comments (hasManyThrough)
you can use load (Lazy eager loading) method
there is an piece of code you can use here
$user->load(["comments" => function($q){
$q->select("comment_id","comment_content");
}]);
read more about Lazy eager loading: docs
I want to get the first related model. But this works only for the first model in the collection. The 2nd is empty.
I've found this answer, but I didn't find a solution.
How can I only get the first related model?
$querybuilder->with([
'messages' => function ($query) {
$query->orderBy("created_at", "DESC");
$query->limit(1);
}
]);
You can use a HasOne relationship:
class Conversation extends Model {
public function latestMessage() {
return $this->hasOne(Message::class)->latest();
}
}
$querybuilder->with('latestMessage');
Be aware that this will still fetch all messages from the database. It then discards the "old" ones.
If you want to improve the performance by really only fetching the latest message, you can use this package I created: https://github.com/staudenmeir/eloquent-eager-limit
The package allows you to apply limit() to the relationship:
class Conversation extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
public function latestMessage() {
return $this->hasOne(Message::class)->latest()->limit(1);
}
}
Your with() actually creates several queries, the last query has the limit, hence the behavior (which is correct). you can use \DB::enableQueryLog();, run your query and then \DB::getQueryLog(); to see how the queries are built.
If you instead want to apply a limit to each model item you could fetch all items and iterate over them to fetch one or more related model items
This is not done i sql but in php (laravel collection method), if you need it in sql you could just join your related model and set it up however you want.
This will cause performance issues if you have large amount of data, but if you don't it's quite convenient.
$result = \App\YourModel::all()
->map(function ($item) {
return $item->YourRelatedModel()
->orderBy('someField')
->first();
});
I forgot ... the above only returns the related model's items, if you also want the parent model you can
$result = \App\YourModel::all()
->map(function ($item) {
$item->YourRelatedModelName = $item
->YourRelatedModel()
->orderBy('someField')
->first();
return $item;
});
This is my Report Model
protected $fillable = [
'site_url',
'reciepients',
'monthly_email_date'
];
public function site()
{
return $this->belongsTo('App\Site');
}
This is my Site Model
public function report()
{
return $this->hasMany('App\Report');
}
This is my ReportController
public function showSpecificSite($site_name)
{
$records = DB::table('reports')
->select('email_date','url','recipient')
->whereHas('sites', function($query){
$query->where('site_name',$site_name);
})
->get();
return view('newsite')->with('records',$records)
->with('site_name',$site_name);
}
My Controller is not yet working as well.
The thing is I would like to copy all the three files from sites table to reports table.
Is it possible in insertInto ?
My code on ReportController shows you that I'm selecting data from reports table but I am the one who puts data to reports table to see the output but it is not yet working because of the it cant reach out the value of site_name even though I already put a relationship between the two tables.
You're not actually using Eloquent in your controller you're just using the Query Builder (DB). This will mean that you don't have access to anything from your Eloquent models.
Try:
$records = \App\Report::whereHas('site', function($query) use($site_name) {
$query->where('site_name', $site_name);
})->get(['id', 'email_date', 'url', 'recipient']);
I've added id to the list of columns as I'm pretty sure you'll need that to use whereHas.
NB to use a variable from the parent scope inside a closure you need to pass it in using use().
I'm using Laravel 5.4.22 (the newest one). In MySQL, I have two tables, tag_categories and tags, which form a many-to-many relationship. What I need is a query which returns all the tags for the selected categories. I know how to solve this when I have only one object, and I know how to solve this with querying and looping each of those objects, but there has to be a query or eloquent based solution for the whole thing?
I understand the code below doesn't work because I'm using ->belongsToMany on a collection rather than an object, but how to I bridge this gap the simplest way?
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)
->belongsToMany(Tag::Class)->get();
dd($resultingTags);
belongsToMany generally belongs in the model class, not a method called on the fly. When looking to eager load the relationship, you then call the with() method on the query builder.
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
ex:
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
// Query
$users = User::with('roles')->get();
$rolesOfFirstUser = $users->first()->roles;
If you're trying to get all the tags of the given categories, then you should be querying tags, not tag_categories.
Tag::whereHas('categories', function ($query) use ($chosenCategoriesIds) {
$query->whereIn('id', $chosenCategoriesIds);
})->get();
This is One-to-many relation
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->hasMany('App\Tag');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();
If you want define Many-To-Many relation for this case
You need to have 3 tables tags, tag_categories, tag_tag_category
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->belongsToMany('App\Tag', 'tag_tag_category', 'tagcategory_id', 'tag_id');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();
I have 3 tables: orders, codes, events
I want to be able to pull all events that an order has, but there's an intermediary table that acts as a pivot table. I've been trying to use hasManyThrough and belongsToMany (along with withPivot) without any luck.
Examples:
public function events()
{
return $this->belongsToMany('events'); // tried this, fails
return $this->hasManyThrough('events', 'codes'); // tried this, fails
return $this->hasManyThrough('events', 'codes', 'event_id', 'id'); // tried this, fails
}
Any pointers would be great!
That's a belongsToMany setup. First, the first parameter is the name of the related class. Second, since your pivot table doesn't follow the Laravel naming conventions, you need to specify the name of the pivot table in your relationship definition:
public function events()
{
// first parameter is the name of the related class
// second parameter is pivot table name
return $this->belongsToMany(Event::class, 'codes');
}
With this setup, you can do:
// get an order
$order = Order::first();
// has all the events related to an order
$events = $order->events;
There are many ways to do this. I will show a one you can get it done.
In Order.php model
public function codes(){
return $this->has('App\Http\Code');
}
In Code.php model
public function orders(){
return $this->belongsTo('App\Http\Order');
}
public function events(){
return $this->hasMany('App\Http\Event');
}
In Event.php model
public function codes(){
return $this->belongsTo('App\Http\Code');
}
Then in you Controller, call them to get required data.
In your case you can do it like below:
$orders = Order::with(['codes' => function($q){
$q->with('events');
})->get();
May be you can get them with nested manner(not sure about this because i didn't tried before posting):
$orders = Order::with('codes.events')->get();
put return $orders; in your controller to see the query.
Enjoy!