Laravel 5.3 eloquent 3 table join using models - laravel

I read this post which helped, but need more guidance please.
I need a set of Results for a particular $batteryid (on Result) and a particular $age (on Test model) and a particular $gender (from Athlete). Each Test belongs to one athlete. Each test has many results.
Model Battery:
class Battery extends Model{
public function results(){
return $this->hasMany('App\Result');
}
}
Model Result:
class Result extends Model{
public function test(){
return $this->belongsTo('App\Test', 'test_id');
}
public function battery(){
return $this->belongsTo('App\Battery', 'battery_id');
}
}
Model Test:
class Test extends Model{
public function athlete(){
return $this->belongsTo('App\Athlete', 'athlete_id');
}
public function results(){
return $this->hasMany('App\Result');
}
}
I get the correct results with the following, but it's two queries:
$tests = Test::whereHas('Athlete', function($query) use($gender){
$query->wheregender($gender);
})->where('age', $age)->get();
$test_ids = $tests->pluck('id');
$results = Result::where('battery_id', '=', $battery)
->whereIn('test_id', $test_ids)
->get();
I'd like to use model approach, but am completely stuck. test.age not recognised and not sure how to get the age from athlete model. My attempt:
$results = Result::with('test')
->where('test.age', '=', $age)
->join('batteries', 'batteries.id', '=', 'test.battery_id')
//->where('test->athlete().gender', '=' $gender)
->where('battery_id', '=', $battery)
->get();

You can use whereHas for the condition and with method for eager loading it.
$results = Result::whereHas('test', function($q) use ($age) {
$q->where('age', '=', $age);
$q->whereHas('athlete', function ($q) {
$q->where('gender', 'male');
});
})
->with('test')
->whereHas('battery', function($q) use($battery) {
$q->where('battery_id', '=', $battery);
})
->with('battery')
->get();

Related

How to get top 5 tags of Category from posts Laravel

Hi I've 3 Modeles Post, Tag, and category , post belongs to many categories as well as tags. I want to fetch the top 5 tags of the posts for the given categories but there is no direct relationship between tags and categories.
Following is my eloquent relationship
// Post model
public function categories()
{
return $this->belongsToMany(Category::class, 'post_categories', 'post_id', 'category_id')->withTimestamps();
}
public function tags()
{
return $this->belongsToMany(Tag::class, 'post_tags', 'post_id', 'tag_id')->withTimestamps();
}
// Category Model
public function posts()
{
return $this->belongsToMany(Post::class, 'post_categories', 'category_id', 'post_id')->withTimestamps();
}
// Tag Model
public function posts()
{
return $this->belongsToMany(Post::class, 'post_tags', 'tag_id', 'post_id')->withTimestamps();
}
I have category $category = Category::find($id) for this $category I'm trying to get top 5 tags based on the posts related to this category
Please guide, Thanks in advance
How can I get the top 5 category tags from posts in Laravel?
Controller
$tags = Tag::whereHas('posts' , function (Builder $query) {
$query->whereHas('categories', function ($query,$categoryId) {
$query->where('id', $categoryId);
});
)})
->latest()
->limit(5)
->get();
Alright, I end up using joins
$tags = DB::table('tags')
->select('tags.id', DB::raw('COUNT(tags.id) as tags_count'), DB::raw('FIRST(tag.name)'))
->leftJoin('post_tags', 'tags.id', '=', 'post_tags.tag_id')
->leftJoin('posts', 'posts.id', '=', 'post_tags.post_id')
->leftJoin('post_categories', 'posts.id', '=', 'post_categories.post_id')
->leftJoin('categories', 'categories.id', '=', ''post_categories.category_id')
->where('categories,id', '=', $categoryId)
->groupBy('tags.id')
->orderBy('tags_count', 'DESC')
->limit(5)
->get();

Sorting eloquent model by many-to-many relationship model in Laravel

I'm building an application on Laravel 5.8 where I'm having a model named company, technical_description, state, region. A company can have many locations in many state, so company and state/region are having many-to-many relationship. So my model looks something like this:
class Company extends Model {
use SoftDeletes;
protected $guarded = [];
public function states()
{
return $this->belongsToMany('App\State', 'company_state', 'company_id', 'state_id');
}
public function regions()
{
return $this->belongsToMany('App\Region', 'company_region', 'company_id', 'region_id');
}
public function technicalDescription()
{
return $this->hasOne('App\TechnicalDescription', 'company_id', 'id');
}
}
I'm now building a data-table, where I'm having Company Name, Technical Details, State, Region as table headers. I want to sort according to these relationships, so I tried in controller:
public function companies() {
return CompanyResource::collection(
Company::when($request->name, function ($q) use($request) {
$q->where('name', 'like', '%' . $request->name .'%');
})
->join('company_technical_description', 'companies.id', '=', 'company_technical_description.company_id')
->when($request->sort_by_column, function ($q) use($request) {
$q->orderBy($request->sort_by_column['column'], $request->sort_by_column['order'] );
}, function ($q) use($request){
$q->orderBy('updated_at', 'desc');
})
->paginate();
)
}
So in this case if I want to sort with any details of technical description it will be easy by simply pushing company_technical_decription.established_date in $request->sort_by_column['column']
But in case of many-to-many relationship of state and region, I'm little stuck on how can I proceed.
Help me out with it. Thanks
I know way with join, worked for me with dynamic order for data tables.
You can try something like this:
public function companies() {
return CompanyResource::collection(
Company::when($request->name, function ($q) use($request) {
$q->where('name', 'like', '%' . $request->name .'%');
})
->select('companies.*', 's.name as state_name')
->join('company_technical_description', 'companies.id', '=', 'company_technical_description.company_id')
->join('company_state as cs', 'company_state.company_id', '=', 'companies.id')
->join('states as s', 'cs.state_id', '=', 's.id')
->when($request->sort_by_column, function ($q) use($request) {
$q->orderBy($request->sort_by_column['column'], $request->sort_by_column['order'] );
}, function ($q) use($request){
$q->orderBy('updated_at', 'desc');
})
->paginate();
)
}
I didn't test this solution but it's good for start. If you now pass state_name as argument for ordering, ordering should work. But I'm not sure in your database tables and columns so set your join to work.
I think it's good idea.
Good luck!

How to search into hasMany relationship in laravel

I have a model of Company and Dispensary
<?php
// Company.php
public function dispensary()
{
return $this->hasMany(Dispensary::class);
}
// Dispensary.php
public function company()
{
return $this->belongsTo(Company::class);
}
What I want is first get the dispensary of the company which the user belongs.
$auth = \Auth::user();
$userCompany = $auth->company()->get();
$dispensaries = Company::find($userCompany->first()->id)->dispensary;
How should I construct a query where the user can search for the dispensary list where the user belongs.
I have tried using whereHas but it's searching from the Company instead of the Dispensary
Company::find($userCompany->first()->id)
->whereHas('dispensary', function ($query) use ($name) {
$query->where('name', 'like', "%{$name}%");
})->get();
Okay I finally made it to work just by using where clause
$company = Company::find($id)->dispensary()
->where('name', 'like', "%{$name}%")
->where('contact_email', 'like', "%{$email}%")
->get();

Can you remove a clause from the query builder?

Looking at the API for Builder it seems that all parts of the query are kept in properties like $joins, $wheres, $groups etc.
I also see those properties are public.
My use case is for example scopes, let's say (pure fiction)
class User extends Model
{
public function scopeIsSmart($query)
{
return $query->join('tests', 'users.id', '=', 'tests.user')
->where('tests.score', '>', 130);
}
public function scopeIsMathGuy($query)
{
return $query->join('tests', 'users.id', '=', 'tests.user')
->where('tests.type', '=', 'math');
}
}
If I now write
User::query()->isSmart()->isMathGuy()->get();
I will get an error for joining the same table 2 times. What is a good way to make the joins array unique? (no duplicate joins)
You can check for existing JOINs:
public function scopeIsMathGuy($query)
{
if (collect($query->getQuery()->joins)->where('table', 'tests')->isEmpty()) {
$query->join('tests', 'users.id', '=', 'tests.user');
}
$query->where('tests.type', '=', 'math');
}
You can also create a helper like joinOnce() (see this PR).
Try this:
$query = Test::query();
if ($request['is_smart']) {
$query->where('score', '>', 130);
}
if ($request['is_math']) {
$query->where('type', 'math');
}
$result = $query->with('users')->all();
$users = $result->get('users');

Laravel: getting an array inside an objet bei joinig two tables

I have two tables: films and actors. Thy have a n-m relation.
I have a connection table actor_film with two columns film_id and actor_id.
In order to get the list of films added in the current month with the actors that play in each film I did this:
$current_month_film = DB::table('films')
->join('actor_film', 'actor_film.film_id', '=', 'films.id')
->join('actors', 'actors.id', '=', 'actor_film.actor_id')
->select('films.*', 'actors.name as actors_name')
->whereMonth('films.created_at', '=', Carbon::now()->month)
->orderBy('films.created_at', 'desc')
->groupBy('films.name')
->get();
return view('statistics.month')->withCurrent_month_film($current_month_film);
I get 40 films each of it showing just ONE actor, even if I know there are several actors in one film.
If I delete the "->groupBy('films.name')" I get 132 record: one film several times and each time one actor.
How could I get a list of actors inside each film, joinig the two tables? something like:
[
{
film.id: 1,
film.name,
film.actors
{
actor[1].name: Morgan Freeman,
actor.[2].name: Meryl Streep
actor [n].name: ....
}
},
{
film.id: 2,
film.name,
film.actors
{
actor[1].name: Morgan Freeman,
actor.[2].name: Meryl Streep
actor [n].name: ....
}
}
]
If you want to use Eloquent and you have your relation in place then your can do it as:
Film::whereMonth('created_at', Carbon::now()->month)
->orderBy('created_at', 'desc')
->with(['actors' => function($q) {
$q->select('name');
}])
->get()
Docs
As you're using Laravel, you should make the use of Laravel Relationships. You should have a models like this:
class Actor extends Model
{
public function films() {
return $this->belongsToMany(Film::class, 'actor_film');
}
}
class Film extends Model
{
public function actors() {
return $this->belongsToMany(Actor::class, 'actor_film');
}
}
and to get the film with their respective actors, you can use with method like this:
$films = Film::with(['actors' => function($q) {
$q->select('name');
}])
->whereMonth('created_at', Carbon::now()->month)
->orderBy('created_at', 'desc')
->get();
foreach($films as $film) {
$film_name = $film->name;
$actors_arr = $film->actors->toArray();
}
See more about Laravel's Eloquent Relationships & Eager Loading
Hope this helps!
In Controller
$current_month_film = Film::select('*')->with(['actor_film','actors'])
->whereMonth('films.created_at', '=', Carbon::now()->month)
->orderBy('films.created_at', 'desc')
->groupBy('films.name')
->get();
In App\Model\Film Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Film extends Model
{
//
protected $primaryKey = "id"; //primary key
protected $table = 'film';
public function actor_film(){
return $this->belongsTo('App\Model\ActorFilm','film_id','id');
}
public function actors(){
return $this->hasMany('App\Model\Actors','id','actor');
}
}

Resources