Join head table with position table with laravel eloquent - laravel

I have the following construction:
(Laravel Version 5.4)
I have a head table (series), and a table with associated positions (posts). The positionstable has three values whose determine, whether the posts should be displayed, or even not. The series table has this information as well.
These columns are: 'published' (boolean), 'published_from' (datetime) and 'published_to' (datetime).
To display the posts, the series and the posts, had to be published (published = 1), and the actual date/time has to be grater, or euqal, than the published_from value, of the series and the post, and the actual date/time has to be lower or equal than the published_to value, as well.
To chek whether the postion should be displayed, I wrote a eloquent scope:
public function scopePublishedRestriction($query)
{
$date = Carbon::today();
$today = date('Y-m-d', $date->getTimestamp());
$query->accessible()
->join('learning_series', function ($join) use ($today) {
$join->on($this->table . '.learning_serie_id', 'learning_series.id')
->where('learning_series.published', 1)
->whereDate('learning_series.published_from', '<=', $today)
->where(function ($join) use ($today) {
$join->orWhere(function ($join) use ($today) {
$join->whereDate('learning_series.published_to', '>=', $today);
})
->orWhere('learning_series.published_to', '0000-00-00 00:00:00');
});
})
->where($this->table . '.published', 1)
->where($this->table . '.published_from', '<=', $today)
->where(function ($query) use ($today) {
$query->orWhere(function ($query) use ($today) {
$query->whereDate($this->table . '.published_to', '>=', $today);
})
->orWhere($this->table . '.published_to', '0000-00-00 00:00:00');
});
return $query;
}
The created SQL-statement is correct,
select * from `brshop_docs_learning_posts`
inner join `brshop_docs_learning_series` on `brshop_docs_learning_posts`.`learning_serie_id` = `brshop_docs_learning_series`.`id`
and `brshop_docs_learning_series`.`published` = ?
and date(`brshop_docs_learning_series`.`published_from`) <= ?
and ((date(`brshop_docs_learning_series`.`published_to`) >= ?) or `brshop_docs_learning_series`.`published_to` = ?)
where `brshop_docs_learning_posts`.`visible` = ?
and `brshop_docs_learning_posts`.`active` = ?
and `brshop_docs_learning_posts`.`published` = ?
and `brshop_docs_learning_posts`.`published_from` <= ?
and ((date(`brshop_docs_learning_posts`.`published_to`) >= ?) or `brshop_docs_learning_posts`.`published_to` = ?)
and `brshop_docs_learning_posts`.`deleted_at` is null
I think, the merge of the collections is wrong. For example, every post in this collection has the ID of the series. So every column, that exists in the series table and in the posts table has the value of the series table.
Thank you for your help ;-)
Markus

I solved it with whereHas()
public function scopePublishedRestriction($query)
{
$date = Carbon::today();
$today = date('Y-m-d', $date->getTimestamp());
$query->accessible()
->whereHas('serie', function($query) use($today){
$query->where('learning_series.published', 1)
->whereDate('learning_series.published_from', '<=', $today)
->where(function ($query) use ($today) {
$query->orWhere(function ($query) use ($today) {
$query->whereDate('learning_series.published_to', '>=', $today);
})
->orWhere('learning_series.published_to', '0000-00-00 00:00:00');
});
})
->where($this->table . '.published', 1)
->where($this->table . '.published_from', '<=', $today)
->where(function ($query) use ($today) {
$query->orWhere(function ($query) use ($today) {
$query->whereDate($this->table . '.published_to', '>=', $today);
})
->orWhere($this->table . '.published_to', '0000-00-00 00:00:00');
});
return $query;
}

Related

Get records between two columns using range between two dates, Also search filter criteria

I want to get records that are joined between this date period (2022-02-15, 2022-08-20)
$users = User::whereDate('start_at', '>=', $startDate)
->whereDate('end_at', '<=', $endDate)
->get();
$_start_date = '2022-02-15';
$_end_date ='2022-08-20';
$users = User::where(function ($query) use ($_start_date, $_end_date) {
$query->where(function ($query) use ($_start_date, $_end_date) {
$query->whereRaw("start_date >= date('$_start_date')")
->whereRaw("end_date <= date('$_end_date')");
})
->orwhere(function ($query) use ($_start_date, $_end_date) {
$query->whereRaw("start_date <= date('$_start_date')")
->whereRaw("end_date >= date('$_end_date')");
});
})->get();
try above code this will also return between dates of start_date and end _date e.g below
$_start_date = '2022-02-18';
$_end_date ='2022-08-20';
and also works with below dates e.g
$_start_date = '2022-02-16';
$_end_date ='2022-08-18';
codes look like this in ide
You can use the whereBetween Laravel function:
$users = User::whereBetween('start_at', [ $startDate, $endDate])->get();
Note $startDate and $endDate must be instance of Carbon.
$startDate = new Carbon('paste_your_start_date')->format('Y-m-d')." 00:00:00";
Update
This would be one method to get data from two seperated columns. Example is not tested.
$start_at = Carbon::createFromFormat('Y-m-d H', '2022-02-15 0')->toDateString();
$end_at = Carbon::createFromFormat('Y-m-d H', '2022-08-20 0')->toDateString();
$users = User::whereRaw("start_at <= date('$start_at')")
->whereRaw("end_at >= date('$end_at')")
->get();
Depends on what you need exactly
User::whereDate('start_date', '>=', $startDate)->where('end_date', '<=', $endDate)->get();
or
User::whereDate('start_date', '<', $User)->whereDate('end_date', '>', $startDate)->get();
I think you want to get the records that at least one day from start_date to end_date that exists between 2022-02-15 and 2022-08-20:
$startDate = '2022-02-15';
$endDate = '2022-08-20';
$users = User::whereDate('start_at', '<=', $endDate) // where or whereDate also should work
->whereDate('end_at', '>=', $startDate)
->get();

Multiple Where Clause in Laravel not working as needed

I am trying to get all the expired listings from the db. the table has three fields for the expiry date, to_date_1, to_date_2, to_date_3.
date 2&3 may have a null value but date 1 always has a value. If date 3 is not null I want the query to compare with date 3 and skip date1 &2. if date 3 is null, the query should compare with date 2, otherwise date 1 will be the expiry date. here is my query.
$query = Listing::query()
->where('vendor_id', $vendor->id)
->where('is_deleted', 0)->where('is_published', 1)->where('is_approved', 1)
->where(function ($q) {
return $q->WhereNotNull('to_date_3')->Where('to_date_3', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->where(function ($q) {
return $q->WhereNotNull('to_date_2')->Where('to_date_2', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->where(function ($q) {
return $q->orWhere('to_date_1', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->orderBy('created_at', 'desc');
$listings = $query->paginate(10);
Here is a sample data:
id
from_date_1
to_date_1
from_date_2
to_date_2
from_date_3
to_date_3
1
2021-06-10
2021-08-15
2021-08-16
2021-08-31
2021-09-01
2021-09-15
2
2021-06-25
2021-08-10
2021-08-11
2021-08-22
NULL
NULL
3
2021-06-25
2021-08-20
NULL
NULL
NULL
NULL
if today date is: 21-08-2021, the result should be record: 1 & 2
As Pavlo said group your query inside a where
$query = Listing::query()
->where(function ($query) { // Wrap you code inside a where
$query->where('vendor_id', $vendor->id)
->where('is_deleted', 0)->where('is_published', 1)->where('is_approved', 1)
->where(function ($q) {
return $q->WhereNotNull('to_date_3')->Where('to_date_3', '<', Carbon::now()->format('Y-m-d'));
})
->where(function ($q) {
return $q->WhereNotNull('to_date_2')->Where('to_date_2', '<', Carbon::now()->format('Y-m-d'));
})
->where(function ($q) {
return $q->orWhere('to_date_1', '<', Carbon::now()->format('Y-m-d'));
});
})
->orderBy('created_at', 'desc');
$listings = $query->paginate(10);
Try use orWhere
->where('is_deleted', 0)->where('is_published', 1)->where('is_approved', 1)
->where(function ($q) {
return $q->WhereNotNull('to_date_3')->Where('to_date_3', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->orWhere(function ($q) {
return $q->WhereNotNull('to_date_2')->Where('to_date_2', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->orWhere(function ($q) {
return $q->orWhere('to_date_1', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
More about parameters grouping
UPDATING (where -> orWhere -> orWhere give you your sql)

How to wrap Eloquent query with parenthesis when we use a conditional clauses in Laravel 5.7

I have a query that should load all the posts with just their english translation.
If the user enter a keyword it return just the english post with a title containing that keyword.
if ($searchKeywords||$searchCategory){
$posts = Post::
select('post_translations.post_id AS id', 'post_translations.title AS title', 'category_id', 'locale')
->join('post_translations', 'posts.id', '=', 'post_translations.post_id')
->where(‘post_translations.locale','=','en')
->when($searchKeywords, function ($query, $searchKeywords) {
return $query->where('post_translations.title', $searchKeywords)->orWhere('post_translations.title', 'like', '%' . $searchKeywords . '%');
})
->when($searchCategory, function ($query, $searchCategory) {
return $query->where('category_id', '=', $searchCategory);
->paginate(20);
}
else
$posts = Post::select('id', 'title', 'category_id')->orderBy('title')->paginate(20);
The generated query is this one:
SELECT `post_translations`.`post_id` AS `id`, `post_translations`.`title` AS `title`, `category_id`
FROM `posts` inner join `post_translations`
ON `posts`.`id` = `post_translations`.`post_id`
WHERE `post_translations`.`locale` = 'en'
AND `post_translations`.`title` = 'About'
OR `post_translations`.`title` like 'About’
LIMIT 20 OFFSET 0
That return me all the 3 posts translations of the post About.
This because of the orWhere.
How can I change the eloquent query in order to generate a query like this?
SELECT `post_translations`.`post_id` AS `id`, `post_translations`.`title` AS `title`, `category_id`
FROM `posts` inner join `post_translations`
ON `posts`.`id` = `post_translations`.`post_id`
WHERE `post_translations`.`locale` = 'en'
AND (`post_translations`.`title` = ‘About' OR `post_translations`.`title` like 'About’ )
LIMIT 20 OFFSET 0
The question is not a duplicate of this one because I have one more level of subquery.
How do you wrap Laravel Eloquent ORM query scopes in parentheses when chaining?
Add both condition inside a where query like this:
if ($searchKeywords) {
$posts = Post::select('post_translations.post_id AS id', 'post_translations.title AS title', 'category_id', 'locale')
->join('post_translations', 'posts.id', '=', 'post_translations.post_id')
->where(‘post_translations.locale','=','en')
->where(function ($query) use ($searchKeywords) {
$query->where('post_translations.title', $searchKeywords)
->orWhere('post_translations.title', 'like', '%' . $searchKeywords . '%');
})
->paginate(20);
}
I have solved with this code:
if ($searchKeywords||$searchCategory){
$posts = Post::
select('post_translations.post_id AS id', 'post_translations.title AS title', 'category_id', 'locale')
->join('post_translations', 'posts.id', '=', 'post_translations.post_id')
->when($searchKeywords, function ($query, $searchKeywords) {
return $query->where('post_translations.locale','=','en')
->where(function ($query) use ($searchKeywords) {
$query->where('post_translations.title', $searchKeywords)->orWhere('post_translations.title', 'like', '%' . $searchKeywords . '%');
});
})
->when($searchCategory, function ($query, $searchCategory) {
return $query->where('post_translations.locale','=','en')
->where(function ($query) use ($searchCategory) {
$query->where('category_id', '=', $searchCategory);
});
})
->paginate(20);
}
else
$posts = Post::select('id', 'title', 'category_id')->orderBy('title')->paginate(20);

Is there a way for Eloquent whereHas to only get() the matching results?

I have the following function
public function index(Request $request)
{
$results = Service::whereHas('conditions', function ($query) use ($request){
$query->whereIn('id', $request->conditions);
})
->whereHas('locations', function ($query) use ($request){
$ran = false;
foreach($request->locations as $location)
{
if(!$ran)
{
$query->Where('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
else
{
$query->orWhere('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
$ran = true;
}
})
->with('types', 'contacts', 'locations.datetimes')->toSql();
dd($results);
}
which produces
select * from `services` where exists
(select * from `conditions` inner join `condition_service` on `conditions`.`id` = `condition_service`.`condition_id` where `services`.`id` = `condition_service`.`service_id` and `id` in (13, 14) and `conditions`.`deleted_at` is null)
and exists
(select * from `service_locations` inner join `service_location_service` on `service_locations`.`id` = `service_location_service`.`service_location_id` where `services`.`id` = `service_location_service`.`service_id` and
(`town` like 'manchester' or `city` like 'manchester' or `town` like 'crewe' or `city` like 'crewe')
and `service_locations`.`deleted_at` is null) and `services`.`deleted_at` is null
The problem is the whereHas on locations brings back all results, whereas I only want the result where the town/city is a match. Whats the best way to achieve this?
i think you need to group your location query using the callback which wraps the query in () and just process the first location directly remove it from array and iterate over loop. so below code should work in your case.
$results = Service::whereHas('conditions', function ($query) use ($request){
$query->whereIn('id', $request->conditions);
})
->whereHas('locations', function ($query) use ($request){
$locations = $request->locations;
$query->where(function($builder) use($locations){
$builder->where('town', 'like', '%' . $locations[0] . '%')
->orWhere('city', 'like', '%'.$locations[0].'%');
unset($locations[0]); //remove first item as it is processed
foreach($locations as $location) {
$builder->orWhere('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
});
});
let me know if you find any problem with it. :)
I managed to figure it out late last night.
I moved the with() to the top of the query and made a callback on locations.
public static function newGetSearchResults($request)
{
return Service::with(['types', 'contacts', 'locations.datetimes', 'locations' => function($query) use($request) {
$ran = false;
foreach($request->locations as $location)
{
if(!$ran)
{
$query->Where('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
else
{
$query->orWhere('town', 'like', '%'.$location.'%')
->orWhere('city', 'like', '%'.$location.'%');
}
$ran = true;
}
}])
->whereHas('conditions', function ($query) use ($request){
$query->whereIn('id', $request->conditions);
})->get();
}

Laravel 4 advanced wheres

I have my query working perfectly in mySQL, however am struggling to use the advanced wheres inside the laravel query builder.
Can anyone assist in converting this at all?
So far I have:
LARAVEL
$start_request = '2014-12-18 09:00';
$end_request = '2014-12-18 10:00';
$events= DB::table('events')
->where('hotel_id', 4)
->orWhere(function($query)
{
$query->where($start_request, '<=', 'start_time')
->where($end_request, '>', 'start_time');
})
->orWhere(function($query)
{
$query->where($start_request, '>=', 'start_time')
->where($end_request, '<', 'end_time');
})
->get();
mySQL
SELECT id, title, description, start_time, end_time FROM events where hotel_id = 4 and (('2014-12-18 09:00' <= start_time and '2014-12-18 10:00' > start_time)
or ('2014-12-18 09:00' >= start_time and '2014-12-18 10:00' < end_time));
Can you try this?
$start_request = '2014-12-18 09:00';
$end_request = '2014-12-18 10:00';
$events= DB::table('events')
->where('hotel_id', 4)
->where(function($query) use($start_request, $end_request) {
$query->where(function($subquery) use($start_request, $end_request){
$subquery->where('start_time', '>=', $start_request)
->where('start_time', '<', $end_request);
});
$query->orWhere(function($subquery1) use($start_request, $end_request) {
$subquery1->where('start_time', '<=', $start_request)
->where('end_time', '>', $end_request);
});
})
->get();
Something like this should do the job:
$start_request = '2014-12-18 09:00';
$end_request = '2014-12-18 10:00';
$events = DB::table('events')
->where('hotel_id', 4)
->where(function ($q) use ($start_request, $end_request) {
$q->where(function ($query) use ($start_request, $end_request) {
$query->where($start_request, '<=', 'start_time')
->where($end_request, '>', 'start_time');
})
->orWhere(function ($query) use ($start_request, $end_request) {
$query->where($start_request, '>=', 'start_time')
->where($end_request, '<', 'end_time');
});
})
->select('id', 'title', 'description', 'start_time', 'end_time')
->get();

Resources