represent my sql query in laravel Eloquent - php

I have an application tha i want it to make bookings but i'am having trouble making my Eloquent query
I have two modals "Booking.php" and "Room.php" my bookings migrations table is as follows
Schema::create('bookings', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
$table->integer('phone');
$table->integer('time_in');
$table->integer('room_id');
$table->integer('time_out');
$table->string('days');
$table->string('type');
$table->integer('expiry_status'); /*expiry status 0->expired 1->not expired*/
$table->timestamps();
});
and my rooms migrations table is as follows
Schema::create('rooms', function(Blueprint $table)
{
$table->increments('id');
$table->integer('room_no');
$table->integer('price');
$table->timestamps();
});
in my view i have got inputs for satrt of reservation(time_in) and end of reservation(time_out) now if there is a reservation that does not end before or start after the reservation we want, the room is considered busy therefore i do not want to show that room show those rooms for my reservation dates.
My problem is I want to know the rooms available between my reservation dates,Can someone help me to write the eloquent to get the available rooms from the above table structures. I'm using mysql as the database engine. Thanks in advance.
I found this query to be useful but how to i implement it in the form of Eloquent model
SELECT r.id
FROM rooms r
WHERE r.room_id NOT IN (
SELECT b.room_id FROM bookings b
WHERE NOT (b.time_out < '2012-09-14T18:00'
OR
b.time_in > '2012-09-21T09:00'))
ORDER BY r.room_id;

You can create subselects and advanced where conditions in Laravel Eloquent.
This script worked for me. I tried to build it for your schema.
DB::table('rooms')
->whereNotIn('room_id', function($query)
{
$query->select('room_id')
->from(with(new Booking)->getTable())
->where(function($query)
{
$query->where('time_out', '<', '2012-09-14T18:00')
->orWhere('time_in', '>', '2012-09-21T09:00');
});
})
->orderBy('room_id')
->get();
https://gist.github.com/DengoPHP/d3e95441a7b9e27299b7

Related

Laravel multi relationship

I am working on chat application in laravel/vue and in this app I have users where each user have several rooms each room has at least 2 user in it and several messages I am trying to make relation or query to get all the rooms for each users and the messages in them
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->enum('status',['active','busy','Do Not Disturb']);
$table->timestamp('email_verified_at')->nullable();
$table->string('image')->default('user0.jpg');
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('rooms', function (Blueprint $table) {
$table->id();
$table->string('room_unique');
$table->foreignId('user_id')->constrained()->onDelete('cascade')->onUpdate('cascade');
$table->timestamps();
});
Schema::create('messages', function (Blueprint $table) {
$table->id();
$table->enum('type',['text','image','link']);
$table->string('content');
$table->foreignId('user_id')->constrained()->onDelete('cascade')->onUpdate('cascade');
$table->foreignId('room_id')->constrained()->onDelete('cascade')->onUpdate('cascade');
$table->timestamps();
});
What you have is good. I would just take out the foriegn key user_id from rooms and access user's room through their messages. Hence, messages will be like a pivot table making the relationship between users and rooms many to many.
Hence to access the rooms of a user I would
$rooms = User::find(1)->rooms()->get();
To access users in a room
$users = Room::find(1)->users()->get();
To display messages of a user per room would be like accessing pivot table. Hence
$user = User::find(1);
foreach ($user->rooms as $rooms) {
echo $rooms->pivot->content;
}
So, this is many to many relationship and message is the pivot table. I will stick with the documentation for more details.
The best solution i have found is to make complex relation like this
//relations
public function message()
{
return $this->hasMany(messages::class)->take(1);
}
public function friends()
{
return $this->belongsToMany(User::class, 'friends', 'user_id', 'friend_id');
}
public function rooms()
{
return $this->belongsToMany(Room::class,'user_room','user_id','room_id');
}
public static function getInit()
{
//get room of auth user
$rooms = DB::table('user_room')->where('user_id',Auth::id())->select('room_id')->get()->toArray();
//flatten the array
$roomArray = array_column(json_decode(json_encode($rooms), true),'room_id');
//get users friends and rooms belong to auth user with the last message
$user = User::with(array('friends.rooms' => function($query) use ($roomArray){
$query->whereIn('rooms.id',$roomArray);
},'friends.rooms.messages' => function($query) {
$query->orderBy('created_at','Desc');
}))->whereId(Auth::id())->get()->toArray();
return $user;
}
in getinit I just use this relation after each other like (friends.rooms)
laravel will use friends relation then each result will use rooms relation on it in my case i just need to pick rooms that user and friend have so i limited the relation by using where in rooms belong to the auth user like if we say the auth user has rooms 2,3,4 and his friend has 3,1,5 so the room relation will only return the union of auth user rooms and friend rooms in our case it's 3 then i return messages in each room but for design purpose i just need the last message for each room so i limit my relation message to take 1 and in getinit order them by created date desc

Laravel - Eloquent - How to sum related data and query agaist it?

I'm looking for help with database queries, not collection solution
I have the following models:
User
Order
Product
User hasMany orders, and orders belongsToMany products
I'm in a place where I would need to query users and select all sold products, meaning the sum of all products quantities that are attached to the orders.
quantity value is stored in the order_product pivot table.
Table name: users, orders, products & order_product
Ideally, I would like to make queries like: select all users that have sold at least 100 products, for example.
DB::raw() & selectRaw is most likely the way to go, I think(?), but I'm not sure about the syntax and how to actually make the query, with and without where clause.
Thanks a lot in advance, this has bothered me for a while
Database Schemas
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
});
Schema::create('orders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id')->index();
});
Schema::create('order_product', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('order_id')->index();
$table->unsignedBigInteger('product_id')->index();
$table->integer('quantity')->unsigned()->default(1);
});
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
});
UPDATE
This far I have come:
\App\User::addSelect([
'sold_products_count' => \App\Order::whereColumn('user_id', 'users.id')
->join('order_product', 'orders.id', '=', 'order_product.order_id')
->select(DB::raw('sum(order_product.quantity) as qty')),
])->where('users.sold_products_count', '>=', 100);
HOWEVER, the last statement where('users.sold_products_count', '>=', 100) throws error, cuz there's no sold_products_count column.
So I think I'm on the right track, but how I can use the new sum column in where clause?
Can I use addSelect, or do I have to use something else?
Finally, I solved this
Here's the answer:
\App\User::addSelect([
'sold_products_count' => \App\Order::whereColumn('user_id', 'users.id')
->join('order_product', 'orders.id', '=', 'order_product.order_id')
->select(DB::raw('sum(order_product.quantity) as qty')),
])->having('sold_products_count', '>=', 100);
The idea is to first count the sum via addSelect and then we can query against the value using having, neat
DB::table('users')
->join('orders', 'orders.user_id', '=', 'users.id')
->join('order_product', 'orders.id', '=', 'order_product.order_id')
->select('users.*', DB::raw('sum(order_product.quantity) as qty'))
->having('qty', '>=', 100)
->get();

Only show items that are not in other table laravel

I would like to only show the schools that are not favorited.
to get the ones favorited I use:
$favorite_schools = DB::table('favorite_schools')
->select('favorite_schools.*', 'schools.name')
->leftJoin('schools', 'schools.id', 'favorite_schools.school_id')
->get();
Schools table:
Schema::create('schools', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('active');
$table->timestamps();
});
Favorite_schools table:
Schema::create('favorite_schools', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('school_id');
$table->timestamps();
});
How can I only get the schools that have not been favorited yet?
You need to go the other way. Get the schools, and left join on the favorites table, then get the results that does not have a result in the favorite_schools table.
$favorite_schools = DB::table('schools')
->select('schools.name', 'schools.id')
->leftJoin('favorite_schools', 'schools.id', 'favorite_schools.school_id')
->whereNull('favorite_schools.school_id')
->get();
Add WhereNull('favorite_schools.school_id') to the query you use to get the favorite ones. that will give you the not favorite schools.
Good luck
a slightly different approach would be to use whereNotExists
usage would be something like this:
$schools = DB::table('schools')
->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('favorite_schools')
->whereRaw('favorite_schools.school_id = school.id');
})
->get();

Optimize Laravel Eloquent whereHas() query - very slow

I am using Laravel 5.4
I have 3 models: Order, OrderLine and Product.
Order hasMany() OrderLines
OrderLine hasOne() Product via the product_id in the OrderLine model (I have properly indexed this, at least I think!)
My requirement is to retrieve all Orders and OrderLines where the Product is for a certain brand name.
Here is my eloquent query. I know the query works but it seems to infinitely run when put on a large dataset (circa 10,000 Orders, 12,000 OrderLines/Products)
$orders = Order::whereBetween('order_date', [$this->start_date,$this->end_date])
->whereHas('lines', function ($q1){
$q1->whereHas('product', function ($q2){
$q2->where('brand', 'Brands>SanDisk');
});
})->with('lines')->with('lines.product')->get()->toArray();
This produces the following SQL when debugging via toSql() method.
select
*
from `orders`
where
`order_date` between ? and ?
and
exists (select * from `order_lines` where `orders`.`id` =`order_lines`.`order_id`
and
exists (select * from `products` where `order_lines`.`product_id` = `products`.`id` and `brand` = ?))
My 3 migrations to create the tables are as follows (I have removed anything except keys for simplicity):
Schema::create('orders', function (Blueprint $table) {
$table->increments('id');
});
Schema::create('order_lines', function (Blueprint $table) {
$table->increments('id');
$table->integer('product_id');
$table->integer('order_id');
});
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
});
I then added the following index:
Schema::table('order_lines', function (Blueprint $table) {
$table->integer('product_id')->unsigned()->change();
$table->foreign('product_id')->references('id')->on('products');
});
Results of EXPLAIN syntax as follows:
1 PRIMARY orders ALL 91886 Using where
2 DEPENDENT SUBQUERY order_lines ALL 93166 Using where
3 DEPENDENT SUBQUERY products eq_ref PRIMARY PRIMARY 4 mymemory_main.order_lines.product_id 1 Using where
Try this:
mpyw/eloquent-has-by-non-dependent-subquery: Convert has() and whereHas() constraints to non-dependent subqueries.
mpyw/eloquent-has-by-join: Convert has() and whereHas() constraints to join() ones for single-result relations.
$orders = Order::query()
->whereBetween('order_date', [$this->start_date, $this->end_date])
->hasByNonDependentSubquery('lines.product', null, function ($q) {
$q->where('brand', 'Brands>SanDisk');
})
->with('lines.product')
->get()
->toArray();
That's all. Happy Eloquent Life!

Laravel - A show can have multiple providers

So I am trying to figure a solution to this but not sure exactly how to do this. I have a table that stores all the shows that happen. In a given show I can have multiple providers attend that show. A provider could also attend many shows as well. So how do I store this in the DB and do the eloquent relationship?
Show Schema
Schema::create('shows', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('number')->unsigned();
$table->dateTime('airDate');
$table->string('podcastUrl')->nullable();
$table->timestamps();
});
Provider Schema
Schema::create('providers', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('shortName')->nullable();
$table->string('image')->nullable();
$table->string('social')->nullable();
$table->timestamps();
});
Would I store the provider_id in the shows schema?
Update 1
So I created a new migration for a pivot table
Schema::create('provider_show', function (Blueprint $table) {
$table->integer('provider_id')->unsigned()->index();
$table->foreign('provider_id')->references('id')->on('providers')->onDelete('cascade');
$table->integer('show_id')->unsigned()->index();
$table->foreign('show_id')->references('id')->on('shows')->onDelete('cascade');
$table->primary(['provider_id', 'show_id']);
});
Then in the show model I created the following
public function providers()
{
return $this->belongsToMany(Provider::class);
}
Now when I am saving a new show I added a multiselect to select the providers I want
$show = new Show;
$show->name = $request->name;
$show->number = $request->number;
$show->airDate = $request->airDate;
$show->podcastUrl = $request->podcastUrl;
$show->providers()->attach($request->providerList);
$show->save();
Session::flash('message', "Created Successfully!");
return back();
Then when I save I get the following error
SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: provider_show.show_id (SQL: insert into "provider_show" ("provider_id", "show_id") select 1 as "provider_id", as "show_id" union all select 2 as "provider_id", as "show_id")
Create a provider_show migration which will act as your pivot table.
This table would contain both provider_id and show_id which will provide the many-to-many relationship between those entities.
Then on your Provider model you can provide a shows() method which returns a BelongsToMany relationship.
// In your Provider model
public function shows()
{
return $this->belongsToMany('App\Show');
}
Note that Laravel by default looks for a pivot table name based alphabetically on the two relationships.
You can also add the inverse on your Show model by providing a providers() method that also returns a BelongsToMany relationship.

Categories