Condition on the relational model Laravel 5 - php

Hello and thanks in advance for your time and help. I have 3 very simple tables. A user table with a user_id, a games table with a game_id as well as some other fields (scheduled date/time) and a GamesAttendee table that just has user_id and game_id field. I am trying to select all games that user is connected to and only return ones that are scheduled for the future/past.
What I ended up going with is:
$cur = GamesAttendee::where('user_id',$user_id)->pluck('game_id')->all();
$cur = Game::whereIn('id', $cur)->where('scheduled','>=',$now)->get();
But I feel like there has to be a more efficient way of doing this. I have looked around and tried various things like eager loading and just messing with my models and nothing seems to work. I feel like this a very simple and essential case that is extremely common and I am wondering how this is actually supposed to be done in laravel.
I have tried:
$cur = Game::with(['attendees'=>function($q) use ($user_id){
return $q->where('user_id',$user_id);
}])->where('scheduled','>=',$now)->get();
But that was not what I wanted. I am basically trying to do:
SELECT * FROM GameAttendees
JOIN `games` on games.id = GameAttendees.game_id
WHERE GameAttendees.user_id = 'x' AND games.scheduled >= '2016/05/01' ;
I quickly jotted that mysql code so just ignore any mistakes. Any ideas?
Thank you.
UPDATE:
Resolved by adding the following into my user model:
public function future_games()
{
$now = gmdate('Y-m-d H:i:s',strtotime('+4 hours'));
return $this->belongsToMany('App\Game','games_attendees')->where('scheduled','>=',$now);
}
then in my controller I was able to do:
$future_games = User::with('future_games')->get();

First define many-to-many relation in your Game and User models:
class Game extends Model {
public function users() {
return $this->belongsToMany(User::class, 'GameAttendees');
}
}
class User extends Model {
public function games() {
return $this->belongsToMany(Game::class, 'GameAttendees');
}
}
With that in place you should be able to get all games given user is attending with:
$games = $user->games;
If you want to add some additional conditions, do the following:
$futureGames = $user->games()->where('scheduled','>=',$now)->get();
Or just create another relation in your User model:
class User extends Model {
public function futureGames() {
return $this->belongsToMany(Game::class, 'GameAttendees')->where('scheduled','>=',$now);
}
}
and access them by:
$futureGames = $user->futureGames;

Related

Laravel add condition inside model public function relation

I am developing a booking system which used Laravel 7.0
so in this system I created a table called 'bookings'
and I created its model called Booking.php
inside this Booking.php I put in relation function to join its model to transaction table like below
public function transaction()
{
return $this->belongsTo('App\Models\Transaction', 'id', 'booking_id');
}
suddenly a new requirement coming in, and this booking will have its child
so to differentiate between the booking parent and its child i am adding a new column to the booking table called
'parent_booking_id'
I don't want to change the code function too much so I am thinking on making condition to make sure the relation of the booking points to the correct transaction
I am thinking of code like this to be written
public function transaction()
{
return $this->belongsTo('App\Models\Transaction', 'parent_booking_id' ?? 'id', 'booking_id');
}
but it is not working as intended
the logic is
If the parent_booking_id exists then join the 'booking_id' column at table transaction to 'parent_booking_id' column at table bookings
else
it will join the 'booking_id' column at table transaction to 'id' column at table bookings
I am not really sure if I understood everything.
I not using that kind of logic inside models, but if you really want it. I'm not sure, but I think this could work.
public function parent_booking()
{
$this->belongsTo('App\Models\Booking', 'parent_booking_id');
}
public function transaction()
{
if ($this->has('parent_booking')) {
return $this->hasOne('App\Models\Transaction', 'parent_booking_id', 'booking_id');
} else {
return $this->hasOne('App\Models\Transaction', 'booking_id');
}
}
If ywhat you try to achieve is this:
When a booking have a parent_booking, attach the transaction to the parent_booking, else, attach it to the booking.
To do this, my suggestion is to do things more like this.
public function parent_booking()
{
$this->belongsTo('App\Models\Booking', 'parent_booking_id');
}
public function childs_booking()
{
$this->hasMany('App\Models\Booking', 'parent_booking_id');
}
Then, you can do this in controller:
$booking = Booking::with('parent_booking')->find($id);
if ($booking->has('parent_booking')) {
//do some stuff.
$booking->parent_booking->transaction->associate($transaction->id);
} else {
//do other stuff
$booking->transaction->associate($transaction->id);
}
And for sure, you will want to retrieve the right transaction. There is an example on a retrieve of every bookings.
$bookings = Booking::with(array('parent_booking', function($query) {
return $query->with('transaction');
}))->with('transaction')->get();
foreach($bookings as $booking) {
if ($booking->has('parent_booking')) {
$transaction_to_use = $booking->parent_booking->transaction;
} else {
$transaction_to_use = $booking->transaction;
}
}
You can also perform something like this:
$transactions = Transaction::with(array('booking', function($query) {
return $query->with('childs_booking');
}))->get();
This way you get every transactions with their booking. You also have every child booking.
Finally found the solution to this problem
the proper way to fix this is by
making 2 relation
and then altering code using where condition to make sure it pass the correct relation based on booking value
BUT because this way of fixing require to much code changes i try to find other ways
and the other ways is to instead of creating 'parent_booking_id' i create a column called 'primary_booking_id'
this 'primary_booking_id' will always contain the booking_id of the first booking being made
so on first time create the model I will update so that it gains the booking_id
$booking->update(['primary_booking_id' => $booking->id]);
and then during creation of its child I will put into the primary_booking_id into the creation
$input['primary_booking_id'] => $previous_booking->primary_booking_id;
$booking::create($input);
so the way this is handled require to make just minor changes to booking transaction relation function as below
public function transaction()
{
return $this->hasOne('App\Models\Transaction', 'booking_id', 'primary_booking_id');
}
voila!
it works and no major changes involve

Get data from multiple sql tables using eloquent with laravel

What i want to achieve?
Show user which pacts he is following.
What I am trying
I have designed two tables which are 'pacts' & 'pacts_follwers'
Table 'pacts' has the details of all the pacts
Table 'pacts_follwers' has details of users following a particular pact.
These links will give you the images of both the tables
For Schema Refer Images
So how to get pacts that user is following.
What I have tried?
Sql Query
SELECT pacts.*, (SELECT pactsid FROM pacts_follwers WHERE pacts.id = pacts_follwers.pactsid
and pacts_follwers.userid = 2 ) as pactID FROM `pacts`
Sql query Result
This query will give pactId some value, where the value is null means the user is not following that pact. If this is the solution then i would need Eloquent for this which i am unable to make.
1st table pacts
id
title
about
created_at
updated_at
pactsImage
2nd table pacts_follwers
id
pactsid
userid
created_at
updated_at
Controller Code
$pacts = DB::select("SELECT pacts.*, (SELECT pactsid FROM pacts_follwers WHERE pacts.id =
pacts_follwers.pactsid and pacts_follwers.userid = ".Auth::id()." ) as pactID FROM `pacts`");
You need to setup hasManyThrough relationship for User and Pact.
class User extends Model {
public function pacts() {
return $this->hasManyThrough(
Pact::class,
PactFollower::class
'userid',
'pactsid'
);
}
}
I don't fully understand if you want to achieve "get user's all pacts" or "if pact is followed by user". Either way, you need to setup related relationships.
Or really simple (and not efficient way)
class Pact extends Model {
public function followers() {
return $this->hasMany(PactFollower::class, 'pactsid')
}
}
Now you can use something like
$userIdsForPact = Pact::followers()->pluck('userid');
if ($userIdsForPact->has($user->id)) {
// your operation
}
Edit: For "if pact is followed by user", you need to setup belongsToThrough relationship. It doesn't come out of the box with Laravel but staudenmeir/belongs-to-through package should serve you well.
After setting the relationship properly, you can use something like this.
Pact::with('user')->get();
Or add some methods in your Pact model:
public function followedByUser($user) {
return $this->users->has($user);
}

Using eloquent relation

I am trying to understand how to effectively use Eloquent relationships to have some high level functions in the model.
I have a subscription app with 2 tables, 'users' and 'subscriptions'.
This is a legacy system so I cannot just change things in any way I want.
Table users (model App\User)
id
email
active (0/1)
join_date
address etc
phone
Table subscriptions (model App\Subscription)
id
user_id
box_id (what the person is subscribed to get)
amount
Users are marked active or not active.
I would like to have a static method on the Subscription model that will give me all the active subscriptions. This data is then fed into other parts of the application.
This is derived by joining subscriptions to users and filtering based on the active column.
The query is like this:
SELECT users.*, subscriptions.*
FROM subscriptions
JOIN users ON users.id = subscriptions.user_id
WHERE users.active = 1
Subscription model
class Subscription extends Model
{
public static function allActive()
{
// This works except it doesn't use the eloquent relationship
return static::where('users.active', 1)
->join('users', 'users.id', '=', 'subscriptions.user_id')
->select('users.*','subscriptions.*')
->get();
}
public function user()
{
return $this->belongsTo(User::class);
}
}
User model
class User extends Authenticatable
{
use Notifiable;
public function subscriptions()
{
return $this->hasMany(Subscription::class);
}
}
I would use it like this:
$subscriptions = \App\Subscription::allActive()->toArray();
print_r($subscriptions);
I have 2 questions.
How do I rewrite the allActive function to use the relationship I already defined? Any solution should generate SQL with a JOIN.
In the returned data, how do I separate the columns from the two separate tables so that it is clear which table the data came from?
Given the relationships you have wired up, to get only active subscriptions from the model class you will have to do it this way:
class Subscription extends Model
{
public static function allActive()
{
$activeSubcriptions = Subscription::whereHas('user', function($query){
$query->where('active', 1) //or you could use true in place of 1
})->get();
return $activeSubcriptions;
}
public function user()
{
return $this->belongsTo(User::class);
}
}
Thats working with closures in Laravel, quite an efficient way of writing advanced eloquent queries.
In the callback function you will do pretty much anything with the $query object, its basically working on the User model since you mentioned it as the first parameter of the ->whereHas
Note that that variable has to have EXACTLY the same name used in declaring the relationship
The above i suppose answers your first question, however its highly recommended that you do most of this logic in a controller file
To answer question 2, when you execute that get() it will return Subscription objects array so to access the info based on columns you will have to go like:
$subscriptions = \App\Subscription::allActive();
foreach($subscriptions as $subscription){
$amount = $subscription->amount; //this you access directly since we working with the subscription object
$box_id = $subscription->box_id;
//when accessing user columns
$email = $subscription->user->email; //you will have to access it via the relationship you created
$address = $subscription->user->address;
}

Search in many to many relation Laravel

In Laravel I have this models:
class User {
public function sites(){
return $this->belongsToMany(Site::class);
}
}
and
class Site extends Model {
public function users(){
return $this->belongsToMany(User::class);
}
}
In DB after migrations I have the table site_user with fields: user_id and sites_id.
Now, how can I retrieve for each users the sites al linked to him?
Thanks for the reply.
Use eager loading
$users = App\User::with('sites')->get();
Please check the link below you need to pass the method name on query like below:
$users = App\Users::with('sites')->get();
for more information please visit:
https://laravel.com/docs/5.4/eloquent-relationships#eager-loading
also this will help you to reduce the load on your query.

Laravel 5 Eloquent ORM multi relation

I'm totally new in Laravel, and I'm trying to create relations between models. My tables are:
patch
id
title
area
id
location
area_patch
id
patch_id
area_id
user_area_patch
id
area_patch_id
plant_id
plant_date
User model, "patchs" function should execute something like this:
SELECT
p.id, p.title, up.plant_id, up.plant_date
FROM
rsst_farming_patch p
JOIN rsst_farming_area_patch pp,
rsst_farming_user_patch up ON pp.id = up.area_patch_id AND p.id = pp.patch_id WHERE up.user_id = 1
my models:
class User extends Model {
public function patchs() {
//return user patchs
}
}
class Patch extends Model {
public function area() {
//return area that this patch belongs to
}
}
class Area extends Model {
public function patchs() {
//return available patchs
}
}
can some one make an example of this? I would like to study it. I was messing around with User model, belongsToMany and hasManyThrough, but no luck.
You may need to modify your table structure slightly to make this happen.
I see in youruser_area_patch table you are trying to link the user, area, and patches together. That's not typically how I've done it in laravel. Normally you use a pivot table to link two items together. So let me suggest something like this:
Does a patch belong to a single user? If so you should add a user_id to the patch table.
patch
id
user_id
area_id
title
Can an a Patch be in multiple areas? I kind of doubt it, so I'd add an area_id as well.
class User extends Model {
public function patchs() {
return $this->hasMany('App/Patch', 'user_id');
}
}
class Patch extends Model {
public function area() {
return $this->belongsTo('App\Area', 'area_id');
}
}
class Area extends Model {
public function patchs() {
return $this->hasMany('App\Patch', 'patch_id');
}
}
Then you can start referencing your patchs like:
$patchs = User::find(1)->patchs()
or the area that a patch belongs to by
Patch::find(1)->area()
and all the patchs in an area by
Area::find(1)->patchs()
Does this help?

Categories