Better approach for nested query - laravel-5

I am very new in Laravel.
I have this query
$city = City::where('slug', $slug)->first();
$city->users = $city->users()->where('gender', 'male')->get();
So I am wondering what is a better approach for this situation. To sum up, I need to find a city and all users belonging to the city where gender = male.
I've tried to do this but didn't work
$city = City::whereHas('users', function($q) {
$q->where('gender', '=', 'male');
})->where('slug', $slug)
->first();
What I am missing?

I belive this is possible with Laravel's Eager Loading.
With the relationships properly set up in your City model class you could just do
$city = City::where('slug', $slug)
->with(['users' => function ($query) {
$query->where('gender', 'male');
}])->first();

Related

Laravel model get all results, but if model has relation then check for certain condition

Assume I have book models and borrow models. They are one-to-many relationships. I want to get a collection of all the books but return the result if the book is borrowed within the current week. But if it's not, then don't display. I've been trying whereHas(), but I think it didn't suit what I'm looking for.
$displayed_books = Books::orderBy('updated_at', 'DESC')
->ifHasborrow('borrow', function ($query) {
$query->where('start_date', '>=', currentweek())
->where('end_date', "<=", currentweek());
})
$displayedBooks = Books::orderBy('updated_at', 'DESC')
->where(function ($query) {
$query->whereHas('borrow', function($query) {
$now = Carbon::now();
$query->where([
['start_date', '>=', $now->startOfWeek()],
['end_date', '<=', $now->endOfWeek()]
]);
$query->orWhereDoesntHave('borrow');
})
})->get();

Query relationship inside the relationship in Laravel Eloquent

I am trying to get the news of a writer according to date passed. I have written function so far:
public static function getNewsByAuthorByDate($id, $year, $limit = 1){
return User::where('id', $id)->has('news')
->with(['news' => function($q) use(&$limit, &$year) {
$q->where('created_at', 'like', '%' . $year . '%')->latest()->limit($limit);
}])
->first();
}
But the news[] is null. Is it possible to get writer news according to date or do I have to try another way?
you can use whereYear
public static function getNewsByAuthorByDate($id, $year, $limit = 1){
return User::where('id', $id)->has('news')
->with(['news' => function($q) use(&$limit, &$year) {
$q->whereYear('created_at',$year)->latest()->limit($limit);
}])
->first();
}
and to use limit with eager loading you should use the package eloquent-eager-limit
According to the documentation:
The limit and take query builder methods may not be used when constraining eager loads.
Also, You don't need to use has method, the news would be empty if the user has no news:
return User::with(['news' => function($q) use($year) {
$q->whereYear('created_at', $year)->latest();
}])
->find($id);

How to get all item same category with Eloquent

Hi all,
I'm building an application in Laravel, I want to get all item in an article category without using this Eloquent.
My code is
$info = Article::find($article_id);
$cate_info = $info->article_categories()->first();
if($cate_info){
$same = Article::with(['article_categories' => function ($query) use ($cate_info){
$query->where('articles_category.ID' ,$cate_info->ID);
}])->where('ID' ,'!=' ,$article_id)->get();
}
And I get all articles. How to solve.
Article and Article_Category is in many to many relationship.
Thanks.
If you want to have all the articles of a particular article_categories you can use whereHas method, now suppose you have name as a field column in categories table, so you can have something like this:
public function getData(Request $request)
{
$category = $request->categoryName;
// $category = 'ABC Category';
$articles = Article::whereHas('article_categories', function($query) use($category) {
$query->where('name', 'like', '%'.$category.'%');
})->get();
return response()->json(['articles' => $articles], 200);
}
Hope this helps.

Best way to return the sum aggregation of a relations property

I have a simple structure where a post has many votes. A vote has an "value" property which is either 1 or -1
When reading all posts I'd love to select this sum for each post into a custom property on post level. Currently i do this
$posts = Post::where('published_at', '<=', $date)
->orderBy('published_at', 'desc')
->simplePaginate(20);
$posts->each(function($post) {
$post->overallRating = $post->getRating();
});
This is fully working, however I think it's not that good to make like 20 queries to the database to read the ratings. Is there a way to simplify this in the actual fetch of the posts?
public function getRating()
{
return $this->votes->sum('value');
}
If you want to keep the votes included in the in the pagination results then I would suggest adding with('votes') so they're at least eager loaded i.e.
$posts = Post::with('votes')
->where('published_at', '<=', $date)
->orderBy('published_at', 'desc')
->simplePaginate(20);
However, if you don't want/aren't bothered about having the votes and you just want the ratings for each post you could add the following scope to your Post model:
public function scopeWithRating(Builder $query)
{
if (is_null($query->getQuery()->columns)) {
$query->select([$query->getQuery()->from . '.*']);
}
$query->selectSub(
$this->votes()->getRelationExistenceQuery(
$this->votes()->getRelated()->newQuery(), $query, new Expression('sum(value)')
)->toBase(),
'rating'
);
}
Then:
$posts = Post::withRating()
->where('published_at', '<=', $date)
->orderBy('published_at', 'desc')
->simplePaginate(20);
Hope this helps!
Try this:
$posts = Post::where('published_at', '<=', $date)
->orderBy('published_at', 'desc')
->with(['votes' => function($query) {
$query->sum('value');
}])->simplePaginate(20);

Ordering Results from Related Tables in Eloquent and the Session

I have the following in my Event model in Laravel 4. The reason I am using QueryBuilder and not Eloquent is I need to have links in each column header in my results table in my view, that when clicked, order the results in asc or desc based on that column.
The issue I'm having is, if I use Eloquent, it won't work as most of the data is pulled through via relationships to other tables, so Eloquent can't find the required columns/fields.
public static function getEvents($perPage = 10)
{
$order = Session::get('event.order', 'start_date.desc');
$order = explode('.', $order);
$columns = array(
'events.id as id',
'title',
'locations.city as city',
'suppliers.name as supplier_name',
'venues.name as venue_name',
'start_date',
'courses.price as course_price',
'type',
'status',
'max_delegates as availability',
'tutors.first_name as tutor_first_name',
'tutors.last_name as tutor_last_name',
'contacts.first_name as d_first_name'
);
$events = DB::table('events')
->leftJoin('courses', 'course_id', '=', 'courses.id')
->leftJoin('suppliers', 'supplier_id', '=', 'suppliers.id')
->leftJoin('locations', 'location_id', '=', 'locations.id')
->leftJoin('venues', 'venue_id', '=', 'venues.id')
->leftJoin('event_types', 'event_type_id', '=', 'event_types.id')
->leftJoin('event_statuses', 'event_status_id', '=', 'event_statuses.id')
->leftJoin('tutors', 'tutor_id', '=', 'tutors.id')
->leftJoin('delegate_event', 'delegate_event.event_id', '=', 'events.id')
->leftJoin('delegates', 'delegates.id', '=', 'delegate_event.delegate_id')
->leftJoin('contacts', 'delegates.contact_id', '=', 'contacts.id')->groupBy('events.id')
->select($columns)
->orderBy($order[0], $order[1])
->paginate($perPage);
return $events;
}
If you look at my EventsController at the getOrder method:
public function getOrder($order)
{
Session::put('event.order', $order);
return Redirect::back();
}
You can see I am storing the order in the session and then in my model using that to sort the order of results.
Is there a way to do this in Eloquent the way I need to?
You know you can just add a join() to eloquent right?
So, as an example, if you have users and each user has some blog posts (i.e. user has_many blogs), you can output an eloquent model showing username and blog title, ordered by blog title, as follows:
user::join('blogs', 'blogs.id','=','users.blog_id')
->order_by('blogs.title', 'asc')
->select(array('users.username', 'blogs.title'))
->paginate(10);

Resources