hi I have the following question. In laravel I created 2 tables, apartments and sponsors with many to many relationships. Depending on the plan chosen, the sponsors have a variable duration that I valued in the pivot table(start_time and end_time). I would need the relationship between the apartment and the sponsor to end automatically after the sponsorship period has ended(when the date and day coincide with the end_time of the pivot table). How could I do such a thing? I thought about using detach but I'm not sure how.
It is always a good idea to look at these problems from different perspectives. Like the two comments mention, you could use Job Scheduling In windows or cron jobs in linux systems.
The next option is to create another field in your pivot which is something like “active”. So this will be a boolean and will always return a true or false. Now you can combine task scheduling and using a custom artisan command, you can keep checking if the sponsor expired and if it did expire, set the active field to false.
And to top it all, when you get your data, simply filter your query. I do not know how your code looks like but here is a sample code of a pivot and how I would have filtered it (not tested):
//Here active null assumes it is false value. Active 1 assumes it is true. So the active field will be nullable
$myQuery = Sponsor::with([‘apartments’ => function($q) {
$q->where(‘active’, null)->get();
}])->get();
//The apartments is assumed to be the pivot relationship in your sponspor model
This is just a simple example of how you could chain and query a relationship, whether be a pivot or any other table, you can do custom queries within like mentioned above. Something along the lines should work in your case
Related
Hello I am working with Laravel,
I have to create two simple models, let's say Stores and Books.
Stores can have one or multiple Books and Books can belong to many Stores.
Of course I will use a many to many relationship, with a pivot table.
Books the can have different prices depending the store.
I think a separate table can only complicate things, in my mind the pivot table associating books and stores should have a price column, but pivot tables only contains store_id and book_id.
Should I create a book_prices and associate it with books and to stores? What is the best approach?
You are free and able to set other attributes on your pivot table. You can read more about it in the docs.
https://laravel.com/docs/9.x/eloquent-relationships#retrieving-intermediate-table-columns
You have to define the relationship accordingly, the following should clarify how this works. In this example you use the many-to-many relationship and add the price column to every retrieved pivot model.
public function books()
{
return $this->belongsToMany(Book::class)
->withPivot('price')
}
For example, you are able to access the pivot column in a loop like this
foreach ($shop->books as $book)
{
echo $book->pivot->price;
}
You can define additional columns for your pivot table in the migration for the pivot table, and then when defining the relationship use withPivot to define the additional columns so they come through in the model:
return $this->belongsToMany(Book::class)->withPivot('price');
(Adapted from the Laravel documentation, see https://laravel.com/docs/9.x/eloquent-relationships#retrieving-intermediate-table-columns)
Depends on the complexity of your case, but yes, you have two options for it. Let's say that the pivot table is called as book_store:
Directly adds price column to book_store. This is obviously the simpler option. The drawbacks are:
The history of the price changes isn't logged. You'll have to create another table for logging if you want to keep this history information.
Changes made to price will directly change the price of the related book_store record. Meaning that a price is being updated "live" e.g users cannot update the price now but "publish" it some time later just like this example in the doc.
Create a new, different table to store the price. This may seems relatively more complex, but it may also be more future-proof.
Basically, you get 2 things that you miss in the first option above.
Don't think too much about book_store being a pivot table. One way to see it is like this: book_store IS a pivot table from books and stores tables viewpoints, but it's also just a normal SQL table which could relate to any other tables using any kind of relationships.
If you want to implement this, make sure to create a primary-key in the book_store table.
Alast, it all depends on what you need. Feel free to ask if you need more insight about this. I hope this helps.
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!
I have a table which has user publications, when the user publish a publications he choose a date that this publication will remains visible in the platform, i want a way to flip the state of a publication that time is come to now be shown anymore (date of publication is equal or greater then date system), i know about the cronjob way but i wanted to ask here if there's a better way to do this because i think the cronjob way is not good for this i will have to run a command every minute which i prefer not to do, i only need ideas not code.
Thank you.
Do not store an active flag on the database. In your case active is a derived attribute and derived attributes should not really be stored as columns, not to mention that since active will be based on a date it is not functionally dependant on the primary key directly so is also in violation of the 2nd normal form. These two reasons will generally lead to data anomalies and what you're trying to do with a scheduled task is basically hack your way around those anomalies.
My suggested approach is to use an eloquent model attribute and attribute mutators like below:
class Publication extends Model {
protected $appends = [ 'active' ];
public function getActiveAttribute() {
return $this->activeUntil < Carbon::now(); //Still active
}
}
This way your model includes the active attribute which is computed by eloquent and not stored in the database.
If i get users company name like this:
Auth::users()->company->name
And if i do it multiple times on same function, it only makes one query for relation and the rest it uses the value retrieved the first time.
I want to know, how deep this goes and is it possible to make it go deeper. If i, for example, use it in controller and then again on the view, does it make a new query? How can i make it so it would only make the same query once per request (page view)?
Clarification:
Question was not about eager loading. The question was about how to avoid making the SAME query more than once. Something that is used a lot, like, users company, users tag, users settings. Data that many parts of my application may need but making a new query every time in different parts of my app feels like wasting resources.
When using Eloquent, if you have a model related to another one, you can eager-load the related models in order to minimize the number of queries.
For example, if you have a Movie model related to a Genre model (a movie belongsTo a genre, a genre hasMany movies). If you have 10 movies:
$movies = Movie::all();
foreach ($movies as $m) echo $m->genre->name;
// 11 queries are gemerated (get all + 1 per item)
whereas :
$movies = Movie::with('genre')->get();
foreach ($movies as $m) echo $m->genre->name;
// 2 queries are gemerated (get all movies + get all genres for those movies)
Note that you can eager-load multiple and/or nested relations.
e.g.: with(['genre', 'actors', 'actors.profile'])
Edit following clarification:
You can then use a middleware which will load your common data. Register that middleware globally so that it can be executed for all requests.
If needed, make an additional facade and a kind of global data manager to make that data available later on.
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());