Check if a column is null in relationship, Laravel - laravel

I try to check if a column from a relationship table is null, . But with my current code I get an error.
The column is "best_match" in the second whereNotNull.
SQLSTATE[42P01]: Undefined table: 7 ERROR: missing FROM-clause entry
for table "questions"↵LINE 1:
$questions = Model::whereNotNull('question_id');
if($excepted_questions){
$questions->whereNotIn('id', $excepted_questions);
}
$questions->where('votes', '!=' , $confidence_specs->votes)
->orWhere('weight', '!=' , $confidence_specs->weight)
->with('questions')
->inRandomOrder();
//HERE I try to check if that column is null or not
if($confidence_specs->best_match){ // this can be true / false ,if is true I check if that column is null
$questions->whereNotNull('questions.best_match');
}
$questions->limit($nr_of_questions)
->get();

You will have to use whereHas to use where statement with the relationship model or you must have joined the table.
Otherwise just using where statement won't work on multiple tables.
For more information check the docs Querying Relationship Existence
So your query should have been
if($confidence_specs->best_match) {
$questions->whereHas('questions', function($query) {
$query->whereNotNull('best_match');
});
}
with will just eager loads the model it doesn't let you perform MySQL query on it.

Related

Update Laravel column based on another column from the same query

I want to update all row that has no 'timemodified' based on another column from the same query.
DB::table('sco_tracks')->where('timemodified', '')->update([
'timemodified' => Carbon::createFromFormat('Y-m-d H:i:s' , DB::raw('updated_at'))->timestamp
]);
With this code I get: Data missing {"exception":"[object] (InvalidArgumentException(code: 0): A four digit year could not be found
the raw query would be
UPDATE sco_tracks t SET timemodified=UNIX_TIMESTAMP(created_at) WHERE timemodified IS NULL;
the code for laravel:
DB::table("sco_tracks")->where('timemodified','')->update(['timemodified'=>DB::raw('UNIX_TIMESTAMP(updated_at)')]);
and if the value of timemodified field is null you can use this code:
DB::table("sco_tracks")->whereNull('timemodified')->update(['timemodified'=>DB::raw('UNIX_TIMESTAMP(updated_at)')]);

Error : Call to a member function where() on int

I want to update data only in pivot table in Laravel 8. All columns are foreign keys. While run the following query, the data in pivot table has been updated but Laravel form give the following error.
Error:
Call to a member function where() on int
Controller:
DB::table("student_topic_examiner")->update([
"internal_id" => $internal->id,
"external_id" => $external->id])
->where("roll_number", "=", $request->input('roll_number'));
Database pivot table is as pivot table.
Please guide.
The problem is that you are calling where() after update().
update() returns a integer (1 if something was updated, 0 otherwise).
It means that you code translates to (1)->where("roll_number", "=", $request->input('roll_number')).
If you only want to update the rows where this condition is true:
->where("roll_number", "=", $request->input('roll_number'))
Then, call it before update():
DB::table("student_topic_examiner")
->where("roll_number", "=", $request->input('roll_number'))
->update([
"internal_id" => $internal->id,
"external_id" => $external->id]
);

Undefined relationship error in sorting of existence and non existence relationship using Eager Load in Laravel eloquent model

I have a Student model and a corresponding one to one mapping relationship to Result model.
I have an eligibleList array containing a list of student id whose marks are to be displayed. Some student have results while some does not have but i need to display all of them from the list.
I am able to retrieve and display students using the following:
$students = Student::with('result:student_id,marks')->whereIn('students.id', $eligibleList)->get();
foreach($students as student) {
if ($student->result != null)
Log::debug($student->result->marks)
else
Log::debug("-1") //-1 indicate no results
}
The above has no issue until i need to sort the list (ascending or descending) by the marks. I tried the following:
$students = Student::with(['result:student_id,marks' => function ($query) {
$query->orderBy('marks','DESC');
}])->whereIn('student.id', $eligibleList)->get();
It throws me a "Call to undefined relationship" error. Is there anyway to sort from the query ? I avoid sorting the collection as it can get very slow for thousands of records. Somehow eloquent early loading encounter some error when sorting with non existence relationship.
you should use 'Subquery Ordering', ordering inside 'with' will not sort the overall result.
$students = Student::with(['result:student_id,marks'])->whereIn('student.id', $eligibleList)
->orderByDesc(Result::select('marks')->whereColumn('student_id','students.id'))
->get();
https://laravel.com/docs/7.x/eloquent#advanced-subqueries
if you use laravel 5 you have to use 'join':
Student::with(['result:student_id,marks'])->whereIn('student.id', $eligibleList)
->join('result','result.student_id','student.id')
->select('user.*,result.marks')->orderBy('result.marks')->get();
'join' use table name not the relation name, so please be careful about table name in previous 'join' and 'select' statements

How can i convert this sql query in a eloquent laravel command?

in sql query this commando do exactly i wanted:
SELECT
v.id,
(
SELECT sv.status_id
FROM status_viagem sv
WHERE sv.viagem_id = v.id
ORDER BY sv.created_at DESC LIMIT 1 ) AS status_id
FROM viagens v
Here is the sql results:
But i have no idea how can i do this using Laravel eloquent
Basically, a viagem entry can has a lot of status, but i need to get each viagem and their last status entry from status_viagem table (the pivot table)
by the way viagem/viagens means travel.
My class mapping:
class Viagem extends Model
{
...
public function status()
{
return $this->belongsToMany('App\Status')->withTimestamps();
}
...
}
class Status extends Model
{
public function viagens()
{
return $this->belongsToMany('App\Viagem')->withTimestamps();
}
}
The belongsToMany at both classes gets me a many-to-many:
can someone help me? thanks
---------- Temporary Solution -------
Thanks for all help guys. In fact i can't find a nice solution using only eloquent.
Step 1/3 - To bypass this situation i first execute the above sql to grab only the viagens under the desired status_id (last status_viagem entry):
$viagens_ids = DB::select(
"SELECT viagem_id FROM (
SELECT
v.id AS viagem_id,
(
SELECT sv.status_id
FROM status_viagem sv
WHERE sv.viagem_id = v.id
ORDER BY sv.id DESC LIMIT 1 ) AS status_id
FROM viagens v
) AS tt
WHERE tt.status_id = {$status->id}"
);
Step 2/3 - then i used the array_map to organize my viagens ids
$a = array_map(
function($obj) { return $obj->viagem_id; },
$viagens_ids
);
Step 3/3 - And at last i used elequent whereIn to fetch my viagens:
Viagem::with( 'status')->whereIn('id', $a)->get();
In fact i have solved the problem by a-old-sashion-way but i not happy with it because i wish i learn how to do it using eloquent. what bad to me.
There are many ways to query in laravel. I have created a test project for you to try. The gist are:
1. Eloquent ORM
Eloquent ORM is Laravel's magic which have some limitations in eager loading - which i just come across while contemplating your question for hours. It wont play nicely with first(), last(), and some more functions in the constrained eager loading closure.
In your case, our almost there can be fixed:
App\Models\Viagem::with(['status' => function($query){
return $query->orderBy('pivot_created_at', 'desc');
}])
->get()
It will return entire field for Viagem and Status including its pivot table (the status_viagem).
However, if you wanted to retrieve only viagem.id and status_viagem.status_id, you can map() it as such:
App\Models\Viagem::with(['status' => function($query){
return $query->orderBy('pivot_created_at', 'desc');
}])
->get()
->map(function($data){
$o = new stdClass();
$o->id = $data->id;
$o->status_id = $data->status->first()->id;
return $o;
});
Please take note that the statement above require sql query to be ran twice. Eager loading basically works by querying all the Viagem first then queries the Status and map them in memory based on the foreign keys. You can observe that replacing get with toSql will only give you the first query. Please enable Query Logging to see the second query.
2. Query Builder
Embarking from Ryan Adhitama Putra answer, you could do something like:
App\Models\Viagem::join('status_viagem', 'viagens.id', '=', 'status_viagem.viagem_id')
->orderBy('status_viagem.created_at', 'desc')
->groupBy(['status_viagem.status_id', 'viagens.id'])
->select(['viagens.id', 'status_viagem.status_id'])
->get();
This query builder approach guaranteed to be ran only once, you can replace the get() with toSql() to see the resulting query.
3. Raw Queries
Throwing DB::raw() can help sometime, but i really did not want to mention it.
I am not sure what viagens and viagem represent, but I think one of the relationships has to be belongsToMany() and the other hasMany().
then after you set relationships correctly, you can use Eloquent like this :
$status_id = Viagem::with('status')->orderBy('created_at', 'desc')->first()->pluck('status_id');
Try this.
$status_id = Viagem::join('status','viagem.id','status_viagem.viagem_id')
->select('viagem.id','status_viagem.status_id')
->get();

Laravel eloquent with relation data (Eager Loading)

I have two database tables items and measurement_units - item has measurement unit.
Now the problem is I want to select a particular column from items and some column from measurement_unit. I want to use Eager loading
e.g.
$items_with_mu = Item::with("measurement_unit")->select(["item_name", "item_stock"])->first();
When accessing measurement_unit. It returns null. Without the select function it returns data(measurement_unit).
$items_with_mu->measurement_unit;
can anyone help me and sorry for my English.
Try this
Item::with(['measurement_unit' => function($q) {
$q->select('id','unit_column'); //specified measurement_unit column
}])
->select('id','measurement_unit_id','item_name')
->get();
If your laravel version is >=5.5 then you can write in a single line
Item::with('measurement_unit:id,unit_column')
->select('id','measurement_unit_id','item_name')
->get()
You have to select the primary column of the main model like below.
items_with_mu = Item::with("measurement_unit")->select(["item_name", "item_stock", "primary_key"])->first();

Resources