How to check if one record is attached to another? - laravel

I have defined a many-to-many relationship between Student and Seminar models. How can I find out if one particular student is attending one particular seminar? Non-working example of what I want to achieve is illustrated below:
$seminar->students()->contains($student->id);
The following error is shown when using the above code snippet:
BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder::contain()'

Try instead:
$seminar->students->contains($student->id);
Presuming $student contains an instance of the Student model you can simplify it further and just use:
$seminar->students->contains($student);
When you add parentheses you're using the Laravel query builder, and you never completed the query. To do it in the manner you had originally you would need:
$seminar->students()->get()->contains($student->id);
This method could be useful if you wanted to add constraints when fetching your students.
But generally you can omit the parentheses and a Collection will be returned, allowing you to use methods like contains.
This has the additional benefit of not re-querying the database once the relationship is loaded, so will generally be a far more efficient means of fetching relationships.
If you haven't already loaded the students and want a database efficient method of checking you could instead use:
$seminar->students()->where('students.id', $student->id)->exists();

In your case the exception is you are calling 'contains()' function (which is for Laravel Collection) on 'Query Builder'. It should be
$seminar->students->get()->contains($student->id);
but this is inefficient since this will retrieve all the students of the seminar.
so instead,
$seminar->students()->wherePivot('student_id', $student->id)->exists();
this method will check in the intermediate table of many to many relationship for particular seminar-student pair, and will return whether exists or not.

The accepted answer is wrong. $seminar->students()->exists($student->id) does not check if the relationship exists. It only checks if the student exists. The student could belong to any seminar and it would still return true.
The correct way to check if a relationship exists without fetching records from the database would be:
$seminar->students()->whereId($student->id)->exists()

Check using query
$seminar->students()->whereKey($student->getKey())->exists();
Check after query (angry), can have memory problems if you have multiple attachments
$seminar->students->contains($student->getKey());
// or
$seminar->students()->get()->contains($student->getKey());

Related

Can this block of code of mine can be improved

[This is to populate data from two tables that one has two foreign keys from the same column as reference on the other table]
https://i.stack.imgur.com/D8fiv.png
[This is my schema for the table with the foreign key]
https://i.stack.imgur.com/eYDL0.png
This is written in laravel and it is working however i have an itchy feeling that this is wrong
As someone else has commented, you should put the code in your question. More context might also be necessary as it's not clear what you are trying to return from your view(). Are you returning $citizen, $family, or both? It would also be helpful to include what version of Laravel you are using. I'll assume you are using the latest Laravel 8.
Your code should work but you are making more work for yourself if you don't utilize Laravel's relationships. If you do, all the query stuff you have in your code can be reduced to just a few short lines of code such as this:
public function view(Citizen $citizen)
{
$citizen->load('family');
return $citizen;
}
Database Migration
You can shorten your migration by using foreignId(). See docs for details
https://laravel.com/docs/8.x/migrations#foreign-key-constraints
Before:
$table->integer('client_id')->unsigned();
$table->foreign('client_id')->references('id')->on('citizens');
After:
$table->foreignId('client_id')->constrained();
Route Model Binding
I'm assuming your view() method is in your controller and is called from a route. Since the one parameter required is the client's id, you can use Route Model Binding and skip fetching the client from the database.
https://laravel.com/docs/8.x/routing#route-model-binding
public function view(Citizen $citizen)
{
// your code
}
Relationships
You seem to have relationships set up but you aren't using them to load data. You should be able to load the related objects without any manual queries.
If you have a belongsTo(One to Many) relationship on the Citizen model to the Family model, you can load it like this:
$citizen->load('family');
or just use it like this
$relationship = $citizen->family->relationship;
I don't know what the relationships between your models are but you should read up on the different relationship types in the docs.
https://laravel.com/docs/8.x/eloquent-relationships

How to use Query Builder to make a relation in an array? Laravel

I would like to make a relation with query builder... I have three tables, and I would like to join the tables for work with the function.. I'm working in a model.. not in a controller
This is my function
public function map($contactabilidad): array
{
$relation = DB::table('tbl_lista_contactabilidad')
->join('tbl_equipo_postventaatcs', 'tbl_equipo_postventaatcs.id', '=', 'tbl_lista_contactabilidad.postventaatc_id')
->join('users', 'users.id', '=', 'tbl_equipo_postventaatcs.asesor_id')
->get();
return [
$contactabilidad->$relation->name,
$contactabilidad->postventaatc_id,
$contactabilidad->rif,
$contactabilidad->razon_social,
$contactabilidad->fecha_contacto,
$contactabilidad->persona_contacto,
$contactabilidad->correo_contacto,
$contactabilidad->numero_contacto,
$contactabilidad->celular_contacto,
$contactabilidad->comentarios,
$contactabilidad->contactado,
$contactabilidad->respuesta->respuesta
];
}
Query\Builder is best thought of as the primary tool used by Eloquent, but is, nontheless, a completely different package. Query\Builder's purpose is to decouple SQL syntax from the logic that feeds into it, whereas Eloquent's purpose is to decouple that logic from table structures and relationships. So only Eloquent supports Model and Relation classes, Query\Builder does not. And what you're asking for has to do with Relations, so in short, you're kind of barking up the wrong tree.
By the way, I'm differentiating 'Query\Builder' here because Eloquent also has its own wrapper for that class called Eloquent\Builder that shares most of the same syntax. For better or for worse, Eloquent attempts to allow the developer to interact with it in a way that's familiar; not having to track a new set of method names even if you've been seamlessly dropped out of Eloquent and into a Query\Builder object via a magic __call method. It also does something similar regarding Eloquent\Collections vs. Support\Collections. But that can make things very confusing at first, because you have to just kind of know what package you're talking to.
So, to answer your question...
Build a Model class for each of your three tables
Apply relationship methods to each one to pre-configure the model with an awareness of your foreign keys
Call on them using lazy or eager-loading
Something else to note is that with() does not ask Eloquent to perform a JOIN. All it does is run the parent query, extract the key values from the result, run the child query using them in an IN() statement, and marrying the results together afterwards. That's what results in nested results. Speaking from experience, it's kind of a mess generating true JOIN statements off Model Relations and keeping the table aliases unique, so it makes sense this package just skips trying to do that (except with pivot tables on many-to-many relations). This also has the added benefit though, that your related tables don't need to live in the same database. A Query\Builder join() on the other hand, as you have there, would present all fields for all tables at the top-level.

Limit to 1 on hasMany-relationship

I have two models that are related to each other. One model contains users, and the other contains all courses and related timestamp of class start. Now the "related key" between them are the 'user_id' which are in both tables. I manage to get out data when having:
return $this->hasMany(ClassInfo::class,'user_id','user_id');
This works just fine. However, since I use the model in a with clause I need to the only one of the classes that starts a given time if start time crashes with another course for the user. I have tried with both:
return $this->hasMany(ClassInfo::class,'user_id','user_id')->take(1);
return $this->hasMany(ClassInfo::class,'user_id','user_id')->limit(1);
But both just give me empty collections, I don't see why that happends?
Is there any way that I can make it return for example the one with the biggest id value from the Class table (id is auto incremental for each course registered on a user).
Thanks for any tips and guidance!

Prevent hydration model objects with eloquent

I would like to understand what happen when using the toArray () method in a case like this:
Ad::query()->with('nominations')->where(['id'=>$id])->get()->toArray();
Eloquent prevents the construction of the collection and the hydration of individual model objects and directly returns an array or eloquent first hydrates the collection and the model objects and then converts everything into an array (thus doing an extra operation)?
If the answer was the second, how can I get the first behavior with eloquent?
I specify with eloquent because it would be nice to be able to do this by continuing to refer in an abstract way to the entities without then mentioning specific database features in the code (for example the name of the tables).
Answering both your questions. The second one will happen. And you can't make Eloquent create an array, you can just convert an object or collection to an array.
Also, your query has a lot of redundant code, you could just do this:
Ad::with('nominations')->find($id)->toArray()

How to create a new record without executing a database query?

I have Question and Answer models. The Question hasMany Answers. Following commands run in the php artisan tinker mode invoke a database query for no apparent reason:
$q = new Question;
$q->answers[] = new Answer; // invokes the below query
// the executed query
select * from `answers` where `answers`.`question_id` is null and `answers`.`question_id` is not null
As you see, there is no need for database call whatsoever. How can I prevent it?
When you do $q->answers, Laravel tries to load all of the answers on the question object - regardless of whether they exist or not.
Any time you access a relationship on a model instance, you can either call it like answers() and get a query builder back, or you can call it like answers without parentheses and Laravel will fetch a collection for you based on the relationship.
You can prevent it easily by doing this:
$q = new Question;
$a = new Answer;
And then, when you're ready to save them, associate them with each other. In its simplest form, that looks like this:
$q->save();
$q->answers()->save($answer);
It's doing that because you're assigning it to the Question object. It wants to see if you're adding an extant record reference. All Laravel Eloquent models contain magic methods for properties, and trying to use them as temporary data storage is a really bad idea unless you've defined a property on them ahead of time for that specific purpose.
Just use a regular array instead and then associate them after the models have been prepared.
Documentation on one-to-many relationships:
https://laravel.com/docs/5.1/eloquent-relationships#one-to-many

Resources