Laravel Scope with join query - laravel

What im trying to do is to make this join into a scope function
$workorders =\DB::table('users')->leftjoin('work_orders','users.id','=','work_orders.user_id')->select('users.id','users.name','users.email','users.status','work_orders.cod','work_orders.deadline'->where('users.id','=','1');
$workorders = $workorders->get();
This is what I've tried with no success...
public function scopeOrders($query){
return $query->join('work_orders','users.id','=','work_orders.user_id')
->select('users.id','users.name','users.email','users.status','work_orders.cod','work_orders.deadline')
->where('users.id','=','1');
}
Expected result using the scope in a query
$usersWO = User::Orders()->get();

The only difference between $workorders = $workorders->get(); and $usersWO = User::Orders()->get(); is the output format. They both are bug-free.
A dd() on the former outputs a collection of stdClass objects with record values as properties.:
A dd() on the latter outputs a collection of models:
To modify the output of the second to look exactly like the first, you'll have to modify the client code since scopes return collections of models.
$usersWO = User::Orders()->get()->map(function ($user) {
return (object) $user->getAttributes();
});

Related

Eloquent how to pass parameters to relationship

The code I'm trying to fix looks like this. I have an Hotel class which is used in a query to get all hotels in an area but it doesn't discard those which are not available. There's a method inside which should be an accessor but it's not written the way I expected it to be:
public function isAvailableInRanges($start_date,$end_date){
$days = max(1,floor((strtotime($end_date) - strtotime($start_date)) / DAY_IN_SECONDS));
if($this->default_state)
{
$notAvailableDates = $this->hotelDateClass::query()->where([
['start_date','>=',$start_date],
['end_date','<=',$end_date],
['active','0']
])->count('id');
if($notAvailableDates) return false;
}else{
$availableDates = $this->hotelDateClass::query()->where([
['start_date','>=',$start_date],
['end_date','<=',$end_date],
['active','=',1]
])->count('id');
if($availableDates <= $days) return false;
}
// Check Order
$bookingInRanges = $this->bookingClass::getAcceptedBookingQuery($this->id,$this->type)->where([
['end_date','>=',$start_date],
['start_date','<=',$end_date],
])->count('id');
if($bookingInRanges){
return false;
}
return true;
}
I wanted to filter out hotels using this query. So this is the query from the controller:
$list = $model_hotel->with(['location','hasWishList','translations','termsByAttributeInListingPage'])->get();
Is it possible to pass the range of days to the function?
By the way the first thing I tried was to use the collection after the query and pass a filter function through the collection and after that paginate manually but although it does filter, but apparently it loses
the "Eloquent" result set collection properties and it ends up as a regular collection, thus it doesn't work for me that way.
Maybe the best approach for that is to create a query scope (source) and put all your logic inside of this function.
after that you can call this scope and pass the dates. Example you will create a query scope and paste your code inside of it.
public function scopeisAvailableInRanges($query, $start_date, $end_date) {
}
then you will invoke this query scope in your controller like this.
$list = $model_hotel::isavailableinranges($start_date, $end_date)->with(['location','hasWishList','translations','termsByAttributeInListingPage'])->get();
keep in mind that inside of your query scope you will return a collection. A collection of all your available hotels.

Get specific values from controller function

I started learning Laravel and I am trying to achieve the following:
Get data from database and display specific field.
Here is my code in the controller:
public function show()
{
$students = DB::select('select * from students', [1]);
return $students;
}
Here is my route code:
Route::get('', "StudentController#show");
That all works for me and I get the following displayed:
[{"id":1,"firstname":"StudentFirstName","lastname":"StudentLastName"}]
How can I get only the "lastname" field displayed?
Thanks in advance!
DB::select('select * from students')
is a raw query that returns an array of stdClass objects, meaning you have to loop through the array and access properties:
$students[0]->lastname
You can also use the query builder to return a collection of objects:
$collection = DB::table('students')->get();
$student = $collection->first();
$student->lastname;
Lastly, using the query builder, you can use pluck or value to get just the last name. If you only have one user, you can use value to just get the first value of a field:
DB::table('students')->where('id', 1)->value('lastname');
I strongly advise you to read the Database section of the Laravel docs.
$students[0]['lastname'] will return the last name field, the [0] will get the first student in the array.
I would recommend creating a model for Students, which would make your controller something like this:
$student = Students::first(); // to get first student
$student->lastname; // get last names
If you only want the one column returned, you can use pluck()
public function show()
{
$last_names= DB::table('students')->pluck('lastname');
return $last_names;
}
This will return an array of all the students' lastname values.
If you want just one, you can access it with $last_names[0]
As a side note, your show() method usually takes a parameter to identify which student you want to show. This would most likely be the student's id.
There are several ways you can accomplish this task. Firstly, I advise you to use the model of your table (probably Students, in your case).
Thus, for example,to view this in the controller itself, you can do something like this using dd helper:
$student = Students::find(1);
dd($student->lastname);
or, using pluck method
$students = Students::all()->pluck('lastname');
foreach($students as $lastName) {
echo $lastName;
}
or, using selects
$students = DB::table('students')->select('lastname');
dd($students);
Anyway, what I want to say is that there are several ways of doing this, you just need to clarify if you want to debug the controller, display on the blade...
I hope this helps, regards!

Laravel How to pass parameter to Accessor method in model via where condition while query building?

I have a Accessor method in Collection Model getSizesAttribute, which returns array of available sizes eg: ['S','L'], Now I need to get Models with have size 'S'. like:
$collections = $collections->where('sizes','S');
But sizes is array, could I manipulate this anyhow so that I could check returns only if sizes have specific size.
I tried making another method getIsSizeAttribute, like:
public function getIsSizeAttribute($size){
return in_array($size,$this->sizes);
}
Now How could I user this in Where condition like
$collections = $collections->where('is_size','S');
Mutators and Accessors only run skin-deep, after the query's already been executed. You could use Collection::filter() as Bangnokia suggests, but that wouldn't give you any performance benefit of actually applying the condition to the initial request.
I think what you're looking for here is a Query Scope. Add something like this to your Model class:
public function scopeSize(\Illuminate\Database\Eloquent\Builder $query, $size)
{
return $query->whereIn('sizes', $this->sizes[$size]);
}
And access it like this:
$collection = $model->size('S')->get();
You should use filter on collection
$collections = $collections->filter(function($item, $index) {
return in_array('S', $item->sizes);
});

Laravel get result from query in side query by Eloquent in one object

I have two tables:
main_presentations
so here i have "id" and "isEnabled";
child_presentations
And here i have "id" , "isEnabled" and "idParent";
I want to select in one object this is my code:
public function MainSlider(MainPresentation $MainPresentations, ChildPresentation $ChildPresentations)
{
$MainPresentations = MainPresentation::where('isEnabled', true)->get();
foreach ($MainPresentations as $MainPresentation) {
$AnArray[] = ChildPresentation::where([
['idParent', $MainPresentation['id']],
['isEnabled', true]
])->get();
}
return $AnArray;
}
but this is the result:
enter image description here
What you are doing is executing a query per result, which can be ineffective when it starts getting bigger.
You can:
Use querybuilder
As it follows, you just build a query starting on ChildPresentation, set a relation to MainPresentation table by id and get the collection
public function MainSlider()
{
$childPresentations = ChildPresentation::join('main_presentations','main_presentations.id','child_presentations.idParent')
->where('child_presentations.isEnabled', true)->where('main_presentations.isEnabled', true)->get();
return $childPresentations;
}
If you want all the MainPresentations with their respective ChildPresentations, only the enables ones.
You can take advantage of Laravel relationships and eager loading.
https://laravel.com/docs/5.6/eloquent-relationships
First, set the relationships in your MainPresentation model
In MainPresentation.php
public function childPresentation {
return $this->hasMany('App\ChildPresentation', 'idParent', 'id');
}
Your MainSlider function would be:
(Btw, no idea why you're receiving two arguments if you're overriding them but doesn't matter)
public function MainSlider() {
$mainPresentations = MainPresentation::with(['childPresentations' => function ($advancedWith) {
child_presentation.isEnabled is true
$advancedWith->where('isEnabled', true);
}])
->where('isEnabled', true)->get()->toArray();
return $mainPresentations;
}
This will return an array of MainPresentations that contain an array of child_presentations, with all their childs.
This translates to two queries:
Select * from main_presentations where isEnabled = true;
Select * from child_presentations where isEnabled= true and id in (in the first query);
Laravel then does background work to create the structure you desire when you write ->toArray()
Note: If you have a $visible array in your MainPresentation model, be sure to add: 'childPresentation' to it, otherwise the toArray will not agregage the childs to the parent.
Second note: I advise following some standards whenever you're writing code, usually functions are named camelCase and variables are camelCase.

Laravel using find() and get() together

I have a table 'tour2s' with 2 rows and when I do:
$tour = Tour2::find(1);
dd($tour);
it returns the tour with 'id' = 1. And it's Object.
I want to turn the object to collection of only attributes of the model, nothing else. And I know that when I use ->get() it returns collection.
But when I am trying:
$tour = Tour2::find(1)->get();
dd($tour);
It returns a collection but of all 2 tour objects (full objects, not only attributes):
I did it like:
$tour = Tour2::find(1);
$tour = collect($tour);
dd($tour);
and now it's what i what - it return a collection of only model attributes (WHAT I WANTED):
SO, my question is why when I used $tour=Tour2::find(1)->get() it returned all tours not only the one with 'id'=1 ?
Passing an array to find() will return a collection.
$tour = Tour2::find([1]);
However, it will be a collection of Tour2 objects, not only the attributes.
Then, if you want only the attributes, you could use $tour->toArray()
You could also do $tour = collect(Tour2::find(1));
And to answer your question, when you use $tour=Tour2::find(1)->get(), Laravel fetch the first tour, and then calling get() on $tour will fetch all other records, so return two tours in your case.
Ok, the main question, as i understand is: "Why when i wrote Tour2::find(1)->get() i receives collection of all records".
when you wrote Tour2::find(1) it assumes that you receive instanse of model Tour2. So we can simple write $tourInstanse->get()
If you go to \Illuminate\Database\Eloquent\Model you can see that here is no method called get() but we have a magic method __call. Look at his implementation:
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
return $this->newQuery()->$method(...$parameters);
}
So, when you call get() method on a model instance you get model`s QueryBuilder (as described in last row) and call get() method on a QueryBuilder. As a result, you receiving all records of that model Class.

Resources