Laravel - latest record from relationship in whereHas() - laravel

This is my first post in here, so please forgive any mistakes :)
I'm currently working on the project of stock management application (Laravel). I came to the point where anything I do doesn't work, so now I beg for help with it.
I have a table with products, of which some are in the relationship with the others. Everything happens in one table. If the product has a child, the child overrides the parent.
products table view
Then, all the queries I run on them use the following logic:
If the item doesn't have any child, use it.
If the item has children, use the latest child (highest id)
Now I have the relationships created in model file:
public function childItems(){
return $this->hasMany('\App\OrderItem','parent_id');
}
public function parentItem(){
return $this->belongsTo('\App\OrderItem','parent_id');
}
public function latestChild(){
return $this->hasOne('\App\OrderItem','parent_id')->orderBy('id','desc')->limit(1);
}
The problem with latestChild() relationship is, that when you run this query:
\App\OrderItem::find(7)->latestChild()->get()
It works fine and returns only one (latest)(id 6) record in relationship - to do it I had to add orderBy and limit to hasOne().
But when I want to use this relationship in scopes, so in whereHas method, it doesn't work properly, as takes any of the children instead of the latest one.
public function scopeDue($query){
return $query->where(function($q){
$q->has('childItems','==',0)->has('parentItem','==',0)->whereDate('due_date','=', Carbon::today()->toDateString())->whereNull('return_date');
})->orWhere(function($q2){
$q2->has('childItems')->has('parentItem','==',0)->whereHas('childItems',function($q3) use($q2){
$q3->whereDate('due_date','=', Carbon::today()->toDateString())->whereNull('return_date');
});
})->with('latestChild');
}
However, with() at the end returns the right record.
I think, the reason it works so is because my relationship latestChild() returns all the children (despite hasOne()) and when i use it in whereHas it ignores the filtering functions I applied.
I know it's a little bit complex from what I describe, but to explain it better I will use an example. Executing the following in tinker
\App\OrderItem::due()->get();
Should return only record id 2, as the number seven has children, where of course id 5 is due, but the latest child is id 6 which is not due.
I hope I've explained it enough to let you help me, as I'm already going crazy with it.
If you have any ideas on how I could achieve what I need by changing exisiting one or changing the whole logic of it, please help!
Thanks,
Darek

Try this one:
->with(
[
'latestChild' => function (HasOne $query) {
return $query->latest('id')->limit(1);
}
]
);

I think the problem is in your latestChild() method where you do a limit(1). Why don't you try the last() method instead?
So:
public function latestChild(){
return $this->hasOne('\App\OrderItem','parent_id')->last();
}
EDIT:
What about returning the value like this:
public function latestChild(){
$item = App\OrderItem::all()->last();
return $item;
}

Related

Call to a member function first() on null

When I try to fetch my user data, I receive the error
Call to a member function first() on null
public function show($id) {
$user=User::findOrFail($id);
$employee = $user->employees->first();
return view('admin.profile')
->with(['employee' => $employee , 'user' => $user]);
}
The problem is probably in your User model.
Check that you have declared the employees relationship:
public function employees()
{
return $this->hasMany(Employee::class); // I'm assuming you have a Employee model with expected column names, but feel free to replace everything with what you actually have in your app
}
If the problem persists, edit your question with your tables structure and your models.
It's very useful to understand the difference between $user->employees and $user->employees().
$user->employees: returns a Collection of employee models, or null if none are found.
$user->employees(): returns a query builder instance that you can chain additional conditions to (where's, etc).
Both options have a first() option available to them, but one is using a Collection method, where the other is using the query builder method.
Some have already suggested this, and I will as well - the safer and simplest solution to your problem is to use the query builder version of the relationship, since there is no risk of the employees() result being null. It also has the added benefit of not needing to load the entire relationship into a collection just to get the first result.
In short: $user->employees()->first(); is the best way to go.

How to select multiple row values coma separated in laravel

I am trying to get all the ids with coma separated while doing eloquent relationship.
So here is my current queries
Divrank::where('division_id', 591)->with('meta')->orderBy('position', 'asc')->get()
Divrank table has a one to many relation with Divrankmeta model. So with meta I am trying to return
public function meta(){
return $this->hasOne(Divrankmeta::class)->selectRaw('id, match_id,divrank_id, sum(won) as won, sum(loss) as loss, sum(draw) as draw, sum(points) as points, sum(matchePlayed) as matchePlayed, sum(totalSets) as totalSets, sum(totalGames) totalGames')
->groupBy('divrank_id');
}
So far this query works fine..
I get the result like this screenshot
Ok so in my Divrankmeta model, I have a column called winAgainst and it can have some ids and some left null. So with the meta relation I want to retrieve winAgainst ids with coma separated string inside meta object.
For better understanding, here is how Divrankmeta table looks like
How can I do this?
Thank you.
The relation you created is one-to-one not one-to-many. That's why you are getting a meta object of the first matched row instead of an array that contains all related meta records.
I never put the modification codes into the eloquent functions. Those codes seem belongs to somewhere else. From my perspective, using "resources" and modifying the data there is a better idea.
If you chose the do so:
// Divrank.php
public function metas()
{
return $this->hasMany('App\Models\Divrankmeta');
}
// Divrankmeta.php
public function divrank()
{
return $this->belongsTo('App\Models\Divrank');
}
// DivrankController
public function index()
{
return DivrankResource::collection(Divrank::with("metas")->all());
}
Create a resource file.
php artisan make:resource DivrankResource
Now, you can modify your Divrank collection on the resource file before your controller returns it.
public function toArray($request)
{
$metaIds = [];
forEach($this->metas as $meta) {
array_push($metaIds, $meta['id']);
}
$this['metaIds'] = $metaIds;
return parent::toArray($request);
}
I'm not able to test this code. But it will probably work. If you don't want to use resources, you can create the same functionality in your controller as well. Bu we like to make controllers as short as possible.
Ok I think I solved it, These are the changes I did. Thanks
return $this->hasOne(Divrankmeta::class)
// ->selectRaw('id, match_id,divrank_id, sum(won) as won, sum(loss) as loss, sum(draw) as draw, sum(points) as points, sum(matchePlayed) as matchePlayed,
// sum(totalSets) as totalSets, sum(totalGames) totalGames')
->select(\DB::raw("id, match_id,divrank_id, sum(won) as won, sum(loss) as loss, sum(draw) as draw, sum(points) as points, sum(matchePlayed) as matchePlayed,
sum(totalSets) as totalSets, sum(totalGames) totalGames, GROUP_CONCAT(winAgainst) as winAgainst"))->groupBy('divrank_id');

Laravel - why is a Model's relations not being loaded?

I have a Laravel 5.3 site and I think maybe I have some weird things going on due to some actions happening in API controllers and some happening in the regular controllers.
Or maybe an issue where at some times I am dealing with a Model, and sometimes with an Eloquent Collection.
The issue is, I am trying to retrieve relations on a Model and am getting null.
For instance, I have course Model that relates to week Model.
In course Model I get week items as
public function weeks()
{
return $this->hasMany(Week::class, 'course_id');
}
In backend, these relations get sent in this way:
$course->load('weeks')
All is good.
But when course item gets deleted and I try and take action in the week controller as
static::deleting(function($course) {
$course->weeks->delete();
});
$course->weeks is null. At that time I see in the database that all is good and this course items does indeed have a week item related, but $course shows 0 relations.
So something odd is happening where $course->webinars is not grabbing the week items related.
Is there something that I am fundamentally doing wrong? Maybe it is because in the models I have these sorts of statements:
protected $table = 'Week';
Are these preventing the relations from being pulled? I always thought that is I had some function in a model that returns relations that those relations would always be available when I use syntax $course->weeks.
Ideas?
Thanks again,
You can simply setup migrations to automatically delete from weeks if you delete a course, provided you have foreign key relationship.
If you have a column course_id in weeks table then add this into your migration
$table->foreign('course_id')
->references('id')->on('courses')
->onDelete('cascade')
I think you can use Observers. In your AppServiceProvider, first register the observer.
public function boot()
{
Course::observe(CourseObserver::class);
}
Now, add an Observer class.
class CourseObserver
{
public function deleting(Course $course)
{
$course->weeks()->delete();
}
}

How to query from database when I have different foreign keys?

I am trying to query data from my database and pass the results to a view called events, the problem I have is that one of my queries will always return the same result because in the where condition the $events_id is the same always. Is there a better way to do the querying? A better logic?
This code is from my controller called EventController:
public function index()
{
$firm_id = DB::table('firms')->where('user_id', auth()->id())->value('id');
$events_id = DB::table('events')->where('firm_id', $firm_id)->value('id');
$events = DB::table('events')->where('firm_id', $firm_id)->get()->toArray();
$actual_events = DB::table('actual_events')->where('event_id', $events_id)->get()->toArray();
return view('events',['events' => $events,'actual_events' => $actual_events]);
}
Since the $events_id is the same every time, the $actual_events will only contain the first result.
The image I have uploaded shows the problem, my table's first three columns are fine. Starting from the fourth they contain repeated values:
As I guess, you need something like this:
$event_ids = DB::table('events')->where('firm_id', $firm_id)->pluck('id');
$actual_events = DB::table('actual_events')->whereIn('event_id', $event_ids)->get()->toArray();
or write about your problem in details and I will try to help you.
you just said that your tables have relation together.
in this case it's better you using the eloquent for that,
first you should type the relations in model of each table like this:
class User extends Authenticatable{
public function cities()
{
return $this->hasmany('App\City'); //you should type your relation
}
}
for relations you can use this link: laravel relationships
after that when you compact the $user variable to your view, you can use this syntax for getting the city value relation to this user: $user->cities;.

Method on user model to check variable on another table

I have a user model, and I want to check if a user has been assigned a mentor.
public function mentorapplication()
{
return $this->hasMany(mentorApplication::class, 'user_id');
}
public function mentorAssigned()
{
return ($this->mentorapplication()->status == "counsellorAssigned");
}
when I call $user->mentorAssigned() in tinker I get an undefined property for status.
Any ideas?
A couple of things here. Firstly, your relationship is has-many, which is one-to-many. When you try to use the relationship you should expect a Collection to be returned, rather than a single entity. Therefore trying to access the ->status property on returned collection won't work.
Secondly, when you're trying to access the relationship like this:
$this->mentorapplication()
You will get a HasMany instance, which would allow you to chain on more constraints to the query, just like ->where(...), or ->orderBy(...). You would need to use something like ->get() or ->first() to actually run the query and get the results you're after. You can omit the () here and Laravel will load the relation for you, returning your collection:
$this->mentorapplication
Now, I don't know exactly what it is you're trying to achieve, but say for instance you wanted to see if at least one of the mentorApplication objects assigned to the user has a status of counsellorAssigned, you could do something like this:
public function mentorAssigned()
{
foreach ($this->mentorapplication as $mentorapplication) {
if ($mentorapplication->status == 'counsellorAssigned') {
return true;
}
}
return false;
}
Or you could use Laravel's Collection methods to help you rather than just doing a loop here. There are many approaches you could take so I will just leave it there.

Resources