I have this problem with Laravel Eloquent.
I have two models - for simplicity's sake named:
A (id, name)
B (id, a_id, created_at)
relationship: A hasMany B
I need to return all B records filtered by these conditions:
A.name = given_name
B.created_at >= given_date
I want to do this by passing in a closure.
I searched through the laravel documentation on models:
https://laravel.com/docs/7.x/eloquent
https://laravel.com/docs/7.x/eloquent-relationships
and found these examples, but how do I put this together?
public function user()
{
return $this->belongsTo('App\User')->withDefault(function ($user, $post) {
$user->name = 'Guest Author';
});
}
function (Builder $builder) {
$builder->where('age', '>', 200);
}
You can do it using whereHas but first you need to define the relationship in your models like so:
A Model
class A extends Model
{
/**
* Get all B for the A.
*/
public function b()
{
return $this->hasMany(B::class);
}
}
B Model
class B extends Model
{
/**
* Get the B's A.
*/
public function a()
{
return $this->belongsTo(A::class);
}
}
Now after we defined the relationships just use whereHas with a Closure to check for extra conditions:
$allB = B::whereHas('a', function (Builder $query) {
$query->where('A.name', $givenName);
})->where('created_at','>=',$givenDate)->get();
You need to write this on A model
class A extends Model
{
public function aToB()
{
return $this->hasMany('App\B', 'a_id', 'id');
}
}
Then you need to write the query
$userData = A::where('name', $givenName)->with('aToB');
$userData = $userData->whereHas('aToB', function($query) use($givenDate){
$query->whereDate('created_at', date('Y-m-d', strtotime($givenDate)));
});
$userData = $userData->get();
dd($userData->aToB);
Related
I have three models, Clinic, Department and ClinicDepartment and has belongsToMany relation called departments from Clinic to Department using ClinicDepartment's table as pivot table, but when i using this relation, scopes from ClinicDepartment not aplying in query.
I decided to make Pivot model calling ClinicDepartmentPivotand apply those scopes to Pivot, but no luck.
Clinic model:
class Clinic extends Model
{
use SoftDeletes, ActiveOnly, HasParent;
public function departments()
{
return $this->belongsToMany(Department::class, 'clinic_departments', 'clinic_id', 'department_id')->using(ClinicDepartmentPivot::class);
}
}
Department Model:
class Department extends Model
{
use SoftDeletes, ActiveOnly;
public function clinics()
{
return $this->belongsToMany(Clinic::class, 'clinic_departments', 'department_id', 'clinic_id')->using(ClinicDepartmentPivot::class);
}
}
ClinicDepartmentPivot Model:
class ClinicDepartmentPivot extends Pivot
{
use ActiveOnly, SoftDeletes;
}
ActiveOnlyScope:
class ActiveOnlyScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where($model->getTable() . '.is_active', true);
}
}
So basicaly I want to apply global scopes to Pivot model, so when I trying to get Clinics of Department, it should check - is ClinicDepartment has is_active = 1 and not deleted.
UPD 1
My scope traits looks like this:
trait ActiveOnly
{
public static function bootActiveOnly()
{
if (!Auth::guard('admin')->check() && strpos(request()->getRequestUri(), 'admin') === false) {
static::addGlobalScope(new ActiveOnlyScope);
}
}
}
Can be used by any model.
Okay, with some workaround I finally found a working solution that does not look scrappy.
public function clinics()
{
return $this->belongsToMany(Clinic::class, 'clinic_departments', 'department_id', 'clinic_id')
->where(function (Builder $query) {
$query->where('clinic_departments.is_active', 1)
->whereNull('clinic_departments.deleted_at');
});
}
Actual query looks like this:
select `clinics`.*, `clinic_departments`.`department_id` as `pivot_department_id`, `clinic_departments`.`clinic_id` as `pivot_clinic_id` from `clinics` inner join `clinic_departments` on `clinics`.`id` = `clinic_departments`.`clinic_id` where (`clinic_departments`.`is_active` = 1 and `clinic_departments`.`deleted_at` is null)
Thanks all for the ideas.
You are missing something here:
To assign a global scope to a model, you should override a given
model's boot method and use the addGlobalScope method
Laravel Docs on scope
Your models should look like these:
class Clinic extends Model
{
use SoftDeletes, ActiveOnly, HasParent;
protected static function boot()
{
parent::boot();
static::addGlobalScope(new ActiveOnlyScope);
}
public function departments()
{
return $this->belongsToMany(Department::class, 'clinic_departments', 'clinic_id', 'department_id')->using(ClinicDepartmentPivot::class);
}
}
class Department extends Model
{
use SoftDeletes, ActiveOnly;
protected static function boot()
{
parent::boot();
static::addGlobalScope(new ActiveOnlyScope);
}
public function clinics()
{
return $this->belongsToMany(Clinic::class, 'clinic_departments', 'department_id', 'clinic_id')->using(ClinicDepartmentPivot::class);
}
}
I have a problem with a many to many relationship and the translations of the terms.
I have 4 tables:
products
- id, price, whatever
products_lang
- id, product_id, lang, product_name
accessori
- id, active
accessori_lang
- id, accessori_id, lang, accessori_name
I'm trying to assign accessories to products with an intermediate table named:
accessori_products
this is the model for Product:
class Product extends Model {
protected $table = 'products';
public function productsLang () {
return $this->hasMany('App\ProductLng', 'products_id')->where('lang','=',App::getLocale());
}
public function productsLangAll() {
return $this->hasMany('App\ProductLng', 'products_id');
}
public function accessori() {
return $this->belongsToMany('App\Accessori', 'accessori_products');
}
}
this is the model for productLng:
class ProductLng extends Model {
protected $table = 'products_lng';
public function products() {
return $this->belongsTo('App\Product', 'products_id', 'id');
}
}
Then I have the model for Accessori:
class Accessori extends Model {
protected $table = 'accessori';
public function accessoriLang() {
return $this->hasMany('App\AccessoriLng')->where('lang','=',App::getLocale());
}
public function accessoriLangAll() {
return $this->hasMany('App\AccessoriLng');
}
public function accessoriProducts() {
return $this->belongsToMany('App\Products', 'accessori_products', 'accessori_id', 'products_id');
}
}
And the model for AccessoriLng:
class accessoriLng extends Model {
protected $table = 'accessori_lng';
public function accessori() {
return $this->belongsTo('App\Accessori', 'accessori_id', 'id');
}
}
I get the results by this:
$products = Product::has('accessori')->with([
'productsLang ',
'accessori' => function ($accessori){
$accessori->with([
'accessoriLang'
]);
}
])->get();
return $products;
but I want to get only the active accessories something like where accessori.active = 1 but I really don't know where to put it. I've tried in different way but I'm stuck on it by 2 days.
IIRC you don't need a model for the intermediate table on your many to many relationships.
If you want to return Products where Accessori is active you can use whereHas on the Product model.
$prod = Product::whereHas('accessori', function($query) {
$query->where('active', 1);
})->get();
Where the $query param will be running on the Accessori model.
You can do the inverse as well with Accessori to Product.
$acessoris = Accessori::where('active', 1)->whereHas('accessoriProduct')->with(['accessoriLang', 'accessoriProducts.productsLang'])->get();
I'm new to Laravel and I'd like to know how to use eloquent model to join a max(date) record...
App\SchemasGroup::find(7)->schemas()->get()->max('schemasVersion')->get();
I find a group of schemas (schemas_groups table) based on ID, then get the schemas from that group (schemas table), and I want to join the 'date' and 'version' field from schemas_versions table with the last version (so, max date or version field) ...
Relations are defined as:
class SchemasGroup extends Model
{
public function schemas() { return $this->hasMany('App\Schema'); }
}
class Schema extends Model
{
public function group() { return $this->belongsTo('App\SchemasGroup'); }
public function versions() { return $this->hasMany('App\SchemasVersion'); }
}
class SchemasVersion extends Model
{
public function schema() { return $this->belongsTo('App\Schema'); }
public function updatedBy() { return $this->belongsTo('App\User','updated_by'); }
}
Getting the user name who updated that last version would also be lovely...
Apparently it was easy with defining chaining models.
class Schema extends Model
{
public function group() { return $this->belongsTo('App\SchemasGroup'); }
public function versions() { return $this->hasMany('App\SchemasVersion'); }
public function latestVersion() { return $this->hasOne('App\SchemasVersion')->latest(); }
}
and then fetching the data with:
App\SchemasGroup::with('schemas.latestVersion.updatedBy')->find($schemaGroupId);
My Model looks like this
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class appraisaltask extends Model
{
//
protected $table = 'empappraisaltask';
/*
* An invoice can has many payments
*
*/
public function ratings(){
return $this->hasMany('App\appraisalrating','empappraisaltask_id')->select(array('comment', 'rating'));
}
}
and I am doing a query in my function like this
public function getUserbasictask(){
$taskwithcomments = appraisaltask::select(array('id','taskname','description','status'))->with( array('ratings' => function($query)
{
// the condition that will be apply on the with relation
$query->where('emp_id','=',Auth::user()->empid);
}))->where('type','=','basic')->get();
return json_encode($taskwithcomments);
}
But I am getting Empty Rating object . ANy suggestion how to do that
If I remove the select() from the rating function in the model I get all the details
Any help would be appreciated
i got your issue,update your ratings function in appraisaltask model
public function ratings()
{
return $this->hasMany('App\appraisalrating','empappraisaltask_id')->select(array('emp_id','comment', 'rating'));
}
and also update query
public function getUserbasictask(){
$taskwithcomments = appraisaltask::select(array('id','taskname','description','status'))
->with(['ratings' => function($query)
{
// the condition that will be apply on the with relation
$query->where('emp_id','=',Auth::user()->empid);
}])->where('type','=','basic')->get();
return json_encode($taskwithcomments);
}
hope it will work.
I have a database with the following tables and relationships:
Advert 1-1 Car m-1 Model m-1 Brand
If I want to retrieve an Advert, I can simply use:
Advert::find(1);
If I want the details of the car, I could use:
Advert::find(1)->with('Car');
However, if I also want the detail of the Model (following the relationship with Car), what would the syntax be, the following doesn't work:
Advert::find(1)->with('Car')->with('Model');
Many thanks
It's in the official documentation under "Eager Loading"
Multiple relationships:
$books = Book::with('author', 'publisher')->get();
Nested relationships:
$books = Book::with('author.contacts')->get();
So for you:
Advert::with('Car.Model')->find(1);
First you need to create your relations,
<?php
class Advert extends Eloquent {
public function car()
{
return $this->belongsTo('Car');
}
}
class Car extends Eloquent {
public function model()
{
return $this->belongsTo('Model');
}
}
class Model extends Eloquent {
public function brand()
{
return $this->belongsTo('Brand');
}
public function cars()
{
return $this->hasMany('Car');
}
}
class Brand extends Eloquent {
public function models()
{
return $this->hasMany('Model');
}
}
Then you just have to access this way:
echo Advert::find(1)->car->model->brand->name;
But your table fields shoud be, because Laravel guess them that way:
id (for all tables)
car_id
model_id
brand_id
Or you'll have to specify them in the relationship.
Suppose you have 3 models region,city,hotels and to get all hotels with city and region then
Define relationship in them as follows:-
Hotel.php
class Hotel extends Model {
public function cities(){
return $this->hasMany(City::class);
}
public function city(){
return $this->belongsTo('App\City','city_id');
}
}
City.php
class City extends Model {
public function hotels(){
return $this->hasMany(Hotel::class);
}
public function regions(){
return $this->belongsTo('App\Region','region_id');
}
}
Region.php
class Region extends Model
{
public function cities(){
return $this->hasMany('App\City');
}
public function country(){
return $this->belongsTo('App\Country','country_id');
}
}
HotelController.php
public function getAllHotels(){
// get all hotes with city and region
$hotels = Hotel::with('city.regions')->get()->toArray();
}
will adding the relation function just ask for the relation needed
public function Car()
{
return $this->belongsTo(Car::class, 'car_id')->with('Model');
}
but if you want a nested relation just use the period in the with
Advert::with('Car.Model')->find(1);
but for multi-relation use the array
Advert::with('Car','Model')->find(1);