SQL - Trying to find available rooms at a specific location (laravel 4) - php

I'm trying to find available rooms via booking table and via a specific loaction.
$not_available = Booking::where('dateFrom', '>=', $arrival)
->where('dateTo', '<=', $depart)
->get(array('gardenId', 'dateFrom', 'dateTo'));
$gardens = Garden::where('province',$location)->get();
foreach ($not_available as $not)
{
$gardenId = $not->gardenId;
$gardens = Garden::where('id','<>',$gardenId)->where('province',$location)->get();
}
I'm always turning up with a wrong result.
Anyone who can help me out?

This should be pretty close. It's eager loading and would give you better performance.
// If you don't have your relationships setup in your model yet.
class Booking extends Eloquent {
public function gardens()
{
return $this->hasMany('Garden', 'gardenId');
}
}
class Garden extends Eloquent {
public $primaryKey = 'gardenId';
public function booking()
{
return $this->belongsTo('Booking');
}
}
// Eager load
$bookings = Booking::where('dateFrom', '>=', $arrival)
->where('dateTo', '<=', $depart)
->with(array('gardens' => function($query)
{
$query->where('province', '=', $location)
}))
->get();
foreach($bookings as $booking)
{
foreach($booking->gardens as $garden)
{
echo $booking->name;
echo $garden->name;
echo $garden->location;
}
}

Related

Laravel - Eloquent - Get a ralationship without foreign key

I'm working on a Laravel project, where i have the models "Reservation" and "Week".
The model "Week" has a starting date and a price, the model "Reservation" has a starting date and an ending date.
I want to be able to do a eloquent selection like this: Reservation::with('weeks')->get(), but if i do something below eloquent doesn't recognize it as a relationship, and i can't use "HasMany" in Reservation model because i don't associate the tables with ids, but only with dates.
How can i get the weeks as a relationship?
class Reservation extends Model
{
public function weeks()
{
return Week::whereDate('starting_date', '>=', $this->starting_date)
->whereDate('starting_date', '<', $this->ending_date)
->orderBy('starting_date')
->get();
}
}
edited: thanks #Tim Lewis
I finally managed it thanks this repository: https://github.com/johnnyfreeman/laravel-custom-relation
The repository is archived and not installable for Laravel 8, so i just copied the files in this folders:
app\Relations\Custom.php
app\Traits\HasCustomRelations.php
This allows me to use Reservation::with('weeks')->get(); with eadger constraints
use App\Models\Week;
use App\Traits\HasCustomRelations;
class Reservation extends Model
{
use HasCustomRelations;
public function weeks()
{
return $this->custom(
Week::class,
// add constraints
function ($relation) {
if($this->starting_date && $this->ending_date) {
$relation->getQuery()
->where('weeks.starting_date', '>=', $this->starting_date)
->where('weeks.starting_date', '<', $this->ending_date);
}
else {
$relation->getQuery();
}
},
// add eager constraints
function ($relation, $models) {
$starting_date = $models[0]->starting_date;
$ending_date = $models[count($models)-1]->ending_date;
$relation->getQuery()
->where('weeks.starting_date', '>=', $starting_date)
->where('weeks.starting_date', '<', $ending_date);
},
// add eager matcher
function ($models, $results, $foreignTable, $relation) {
foreach ($models as $model) {
$model->setRelation($foreignTable, $results
->where('starting_date', '>=', $model->starting_date)
->where('starting_date', '<', $model->ending_date));
}
return $models;
}
);
}
}
You're almost there. Just use an accessor to get the weeks:
public function getWeeksAttribute()
{
return Week::whereDate('starting_date', '>=', $this->starting_date)
->whereDate('starting_date', '<', $this->ending_date)
->orderBy('starting_date')
->get();
}
and you'll be able to get the weeks as if it were any other attribute. If you need to serialize your model to Json, remember to add weeks to the $appends array in your model.

Detach role from the user and delete the user in bulk along with livewire traits

Description
I have a "belongsToMany" eloquent relationship between users and roles . I am creating a CRUD system with capability to do bulk actions. I followed the Livewire Screencasts Bulk Export/Delete and Refactoring For Re-Usability. I have created a trait for the bulk actions, so I'm able to use it out of the box.
I need to detach the role from the user and then delete the user in bulk. I am unable to call roles relationship on a public method property and detach the same. This is how I detach the role for a single user $this->user->roles()->detach(); but I'm unable to do it with $this->selectedRowsQuery->roles()->detach(); in case of bulk user deletion.
Stripped-down, copy-pastable code snippets
Livewire/Backend/UserController
public $showUserBulkDeletionModal = false;
public function confirmDeleteBulk()
{
$deleteCount = $this->selectedRowsQuery->count();
$this->selectedRowsQuery->roles()->detach();
$this->selectedRowsQuery->delete();
$this->showUserBulkDeletionModal = false;
$this->notify('You\'ve deleted '.$deleteCount.' users');
}
public function getRowsQueryProperty()
{
$query = User::query()
->when($this->filters['email'], fn($query, $email) => $query->where('email', 'like', '%'.$email.'%'))
->when($this->filters['role'], fn($query, $role) => $query->whereHas('roles', fn ($query) => $query->where('id', $role)))
->when($this->filters['search'], fn($query, $search) => $query->where('name', 'like', '%'.$search.'%'))
->when($this->filters['date-min'], fn($query, $created_at) => $query->where('created_at', '>=', Carbon::createFromFormat('d/m/Y', $created_at)))
->when($this->filters['date-max'], fn($query, $created_at) => $query->where('created_at', '<=', Carbon::createFromFormat('d/m/Y', $created_at)));
return $this->applySorting($query);
}
Livewire/Traits/WithBulkActions
trait WithBulkActions
{
public $selectPage = false;
public $selectAll = false;
public $selected = [];
public function renderingWithBulkActions()
{
if ($this->selectAll) $this->selectPageRows();
}
public function updatedSelected()
{
$this->selectAll = false;
$this->selectPage = false;
}
public function updatedSelectPage($value)
{
if ($value) return $this->selectPageRows();
$this->selectAll = false;
$this->selected = [];
}
public function selectPageRows()
{
$this->selected = $this->rows->pluck('id')->map(fn($id) => (string) $id);
}
public function selectAll()
{
$this->selectAll = true;
}
public function getSelectedRowsQueryProperty()
{
return (clone $this->rowsQuery)
->unless($this->selectAll, fn($query) => $query->whereKey($this->selected));
}
}
Context
Livewire version: 2.3.6
Laravel version: 8.23.1
Alpine version: 2.8.0
Browser: Chrome
This line won't work:
$this->selectedRowsQuery->roles()->detach();
$this->selectedRowsQuery is a Collection, and your code isn't smart enough to know which instance of roles() you're trying to detach (delete). You simply need to do this in a loop:
foreach ($this->selectedRowsQuery as $queryRow) {
$queryRow->roles()->detach();
$queryRow->delete(); // Can do this here (single delete), or below
}
$this->selectedRowsQuery->delete(); // Can do this here (batch delete), or above
Edit: At the time of the foreach(), $this->selectedRowsQuery is still an instance of the Builder class, which is incompatible with foreach() until a Closure (get(), cursor(), etc.) is passed. To handle this, simply adjust your code as:
foreach ($this->selectedRowsQuery->get() as $queryRow) {
$queryRow->roles()->detach();
...
}
Note: ->get() is more widely used, but ->cursor() is available and generally more performant for larger loops.

How to get relationship counts without loading objects in laravel

I have a model customer and it has many projects. I want to find projects count without including its object.
Customer model includes:
public function numberOfProjects()
{
return $this->hasMany(Project::class)->count();
}
Query in my controller:
$customers = Customer::where(['is_active'=>1])
->with(['customerContactInformation'=> function ($query) {
$query->where('is_active',1);
}, 'numberOfProjects'])
->skip($skip)->take(10)
->get();
Its giving me error:Call to a member function addEagerConstraints() on integer
Try this
Customer Model
public function numberOfProjects()
{
return $this->hasMany(Project::class);
}
Controller
$customers = Customer::where(['is_active'=>1])
->with(['customerContactInformation'=> function ($query) {
$query->where('is_active',1);
}])
->withCount('numberOfProjects') //you can get count using this
->skip($skip)
->take(10)
->get();
That should be work
$customers = Customer::withCount('numberOfProjects')->get();
WithCount on the particular status
$customers = Customer::withCount([
'numberOfProjects',
'numberOfProjects as approved_count' => function ($query) {
$query->where('approved', true);
}
])
->get();
class Tutorial extends Model
{
function chapters()
{
return $this->hasMany('App\Chapter');
}
function videos()
{
return $this->hasManyThrough('App\Video', 'App\Chapter');
}
}
And then you can do:
Tutorial::withCount(['chapters', 'videos'])
Counting Related Models
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models. For example:
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
You may add the "counts" for multiple relations as well as add constraints to the queries:
$posts = App\Post::withCount(['votes', 'comments' => function ($query) {
$query->where('content', 'like', 'foo%');
}])->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;
You may also alias the relationship count result, allowing multiple counts on the same relationship:
$posts = App\Post::withCount([
'comments',
'comments as pending_comments_count' => function ($query) {
$query->where('approved', false);
}
])->get();
echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;
If you're combining withCount with a select statement, ensure that you call withCount after the select method:
$posts = App\Post::select(['title', 'body'])->withCount('comments');
echo $posts[0]->title;
echo $posts[0]->body;
echo $posts[0]->comments_count;

Laravel Eloquent: hasManyThrough or Something Else?

In Laravel 5.7 I have read the Has Many Through documentation but I still can't use it correctly for my case.
Here's the DB:
Analytics
id
data
subscriber_id
Subscribers
id
city_id
Cities
id
name
I need the Analytics model to get data from Analytics with subscribers.id and cities.name
What I have done:
Connected Analytics and Subscribers models
<?php
class Analytics extends Model
{
public function subscriber()
{
return $this->belongsTo('App\Models\Subscriber');
}
}
class Subscriber extends Model
{
public function analytics()
{
return $this->hasMany('App\Models\Analytics');
}
}
Made a request that gets the data from the Analytics table with subscribers data:
$results = Analytics::where('survey_id', '4')
->with('subscriber')
->whereDate('created_at', '>=', $last_survey_date)
->orderBy('data')
->get();
If anybody has any ideas how to get city names, please, share it.
// maybe this will work for you?
class Analytics extends Model
{
public function subscriber()
{
return $this->belongsTo('App\Models\Subscriber');
}
public function cities() {
return $this->hasManyThrough('App\City', 'App\Subscriber');
}
}
I'm not sure if I understood your request correctly. Do you want to get all Analytics of Subscribers of a given City? Or do you want the city name of an analytics' subscriber? Either way, here are both solutions.
To get all analytics for subscribers of a given city:
$city = 'Vienna';
$results = Analytics::query()
->with('subscriber')
->whereHas('subscriber', function ($query) use ($city) {
$query->whereHas('city', function ($query) use ($city) {
$query->where('name', $city);
});
})
->where('survey_id', '4')
->whereDate('created_at', '>=', $last_survey_date)
->orderBy('data')
->get();
Or to get the city name for an analytics record, you have two options. One is to use the Laravel eager loading for relationships, which works, but will probably load a lot of unnecessary data into memory:
$results = Analytics::query()
->with('subscriber.city') // you can nest relationships as far as they go
->where('survey_id', '4')
->whereDate('created_at', '>=', $last_survey_date)
->orderBy('data')
->get();
foreach ($results as $analytics) {
$city = $analytics->subscriber->city->name;
}
The other is to join the tables yourself and select only the necessary data:
$results = Analytics::query()
->join('subscribers', 'subscribers.id', '=', 'analytics.subscriber_id')
->join('cities', 'cities.id', '=', 'subscribers.city_id')
->where('analytics.survey_id', '4')
->whereDate('analytics.created_at', '>=', $last_survey_date)
->orderBy('analytics.data')
->select('analytics.*', 'cities.name as city_name')
->get();
foreach ($results as $analytics) {
$city = $analytics->city_name;
}
Beware that you could use select('analytics.*', 'cities.name'), but this will override a name column selected of the analytics table if one exists. So it is better to use a column alias with as col_alias.
Thank you Nick Surmanidze and Namoshek for your answers!
I have found the way it works last night:
class Subscriber extends Model
{
public function analytics()
{
return $this->hasMany('App\Models\Analytics');
}
public function subscriberCity()
{
return $this->belongsTo('Modules\Directories\Entities\City', 'city_id', 'id');
}
}
class City extends Model
{
public function subscriber()
{
return $this->hasMany('App\Models\Subscriber');
}
}
And the needed result can be get by this way:
$results = Analytics::where('survey_id', '4')
->with(['subscriber' => function($i){
$i->with(['subscriberCity']);
}])
->whereDate('created_at', '>=', $last_survey_date)
->orderBy('data')
->get();

Querying eloquent relationship

I have the following set for relations in my eloquent models/db:
Student has many PhdReport
PhdReport has one Link
I need to get Students where the (single) most recent PhdReport (by the attribute date_to) is more than 6 months and the Link connected to the PhdReport has a status of 'complete'.
I am trying to do this using eloquent relationships and I'm pretty new to the concept of querying relationships so I would like to know if there's a better approach to the one I'm taking.
Here's the relevant code so far:
PhdReport.php
public function link()
{
return $this->belongsTo(\App\Models\Link::class);
}
Student.php
public function phdReport()
{
return $this->hasMany(\App\Models\PhdReport::class);
}
public function latestPhdReport()
{
return $this->hasOne(\App\Models\PhdReport::class)->latest('date_to');
}
/* this doesn't work! */
public function lastPhdReportSixMonthsAgo()
{
$sixMonthsAgo = \Carbon\Carbon::now()->subMonth(6);
return $this->whereHas('latestPhdReport', function ($query) use ($sixMonthsAgo) {
$query->where('date_to', '<=', $sixMonthsAgo);
});
}
This is my best shot at it so far but I am unsure if the first whereHas applies to the second whereHas also?
$sixMonthsAgo = \Carbon\Carbon::now()->subMonth(6);
$students = $this->student
->whereHas('phdReport.link', function ($query) {
$query->where('status', 'complete');
})
->whereHas('latestPhdReport', function ($query) use ($sixMonthsAgo) {
$query->where('date_to', '<=', $sixMonthsAgo);
})
->get();
If I run:
$students = $this->student
->has('lastPhdReportSixMonthsAgo')
->get();
I get:
BadMethodCallException in Builder.php line 1994:
Call to undefined method Illuminate\Database\Query\Builder::getRelated()
Any advice would be much appreciated!
This is my current solution, I haven't had time to refactor yet but I'm sure it can be improved by doing only a query and no filter. Will edit when I get time but thought I'd put it up as it might help someone else. Much credit to #JarekTkaczyk
$monthsAgo = \Carbon\Carbon::now()->subMonth($months);
// Query 1: students with a completed report
$studentsWithCompletedLink = $studentsWithCompletedLink
->whereHas('phdReport.link', function ($query) {
$query->where('status', 'complete');
})
->get();
if ($months != 0) {
// filter through to get students where the report date_to is more than $months months ago
$studentsWithReport = $studentsWithCompletedLink->filter(function ($student) use ($monthsAgo) {
return $student->latestReport->date_to <= $monthsAgo;
});
}
Student.php
public function latestPhdReport()
{
return $this->hasOne(\App\Models\PhdReport::class)->latest('date_to');
}

Categories