Consider a model Employee and a model Project
The employees table has a property type that can be assigned the following values "1", "2", "3", etc.
Project hasMany Employees
public function programmers() {
return $this->hasMany( 'App\Employee' )
->where( 'type', '1' );
} // hasMany programmers
public function testers() {
return $this->hasMany( 'App\Employee' )
->where( 'type', '2' );
} // hasMany testers
public function managers() {
return $this->hasMany( 'App\Employee' )
->where( 'type', '3' );
} // hasMany managers
Instead of these relationships, I would want to have only one:
public function employees( $type_id ) {
return $this->hasMany( 'App\Employee' )
->where( 'type', $type_id );
} // hasMany employees
It would work like this:
$app->get( '/employee', function() {
$project = App\Employee::find( 1 );
return $project->employees( "1" );
} );
However, I am getting the following exception:
ErrorException in Response.php line 402:
Object of class Illuminate\Database\Eloquent\Relations\HasMany could not be converted to string
Look at the contents of the error message:
ErrorException in Response.php line 402:
Object of class Illuminate\Database\Eloquent\Relations\HasMany could not be converted to string
The error is occurring in the Response class. The reason this error is coming up is because you're returning a relationship in your route definition rather than a response:
$app->get( '/employee', function() {
$project = App\Employee::find( 1 );
return $project->employees( "1" );
} );
A Relationship object cannot be converted to a string, and therefore the Response class doesn't know what to do with it.
If you want to examine the result of your relationship query in the browser, you'll need to return a valid response.
Try changing your route to something like this:
$app->get( '/employee', function() {
$project = App\Employee::find( 1 );
return response()->json($project->employees( "1" )->get());
} );
This will output the results of your query to JSON. Notice also the use of get(). This makes sure the relationship query is actually executed.
Related
i have made indirect relation from one model to another in couple of my models.
this is my Work Model:
public function GeoEntities()
{
return $this->hasMany(\App\GeoEntity::class);
}
public function geoLand()
{
$builder = $this->GeoEntities()->where("entity_type", 0);
$relation = new HasOne($builder->getQuery(), $this, 'work_id', 'id');
return $relation;
}
public function geoLandPoints()
{
return $this->geoLand->geoPoints();
}
this return $this->intermediateModel->FinalModel(); would work, if intermediate relation is belongsTo() and returns a relation instance.
but in this case, when geoLand is Empty it produce error:
Call to a member function geoPoints() on null
like below line:
$points = $work->geoLandPoints;
The Intermediate Relation is a hasMany
i want to have this like relation call geoLandPoints and not geoLandPoints() but,
when intermidate models are null, i want an empty relation.
but i can not figure it out, how to achieve this.
with Fico7489\Laravel\EloquentJoin\Traits\EloquentJoin
using Fico7489\Laravel\EloquentJoin\Traits\EloquentJoin package, i have tried to refactor relation like below:
public function geoLandPoints()
{
$builder = $this
->select("works.*")
->join("geo_entities", "works.id", "geo_entities.work_id")
->join("geo_points", "geo_entities.id", "geo_points.geo_entity_id")
->where("entity_type", 0)
->where("works.id", $this->id);
return new HasMany($builder->getQuery(), $this, "work_id", "id");
}
but it couldn't convert Database Query Builder to Eloquent Query Builder.
Argument 1 passed to
Illuminate\Database\Eloquent\Relations\HasOneOrMany::__construct()
must be an instance of Illuminate\Database\Eloquent\Builder, instance
of Illuminate\Database\Query\Builder given
Why don't you use the hasOne() method instead of trying to return your own HasOne class? Also, you can use withDefault() so the relationship returns an empty GeoEntity instead of null.
public function geoLand()
{
return $this->hasOne(\App\GeoEntity::class)->where("entity_type", 0)->withDefault();
}
You could even pass an array of default values. withDefault(['column' => 'value', 'column2' => 'value2', ...])
I have the tables
threads
- id
replies
- id
- repliable_id
- repliable_type
I want to add another column to the Thread, which is the id of the most recent reply.
Thread::where('id',1)->withRecentReply()->get()
And the following query scope
public function scopeWithRecentReply() {
return $query->addSelect([
'recent_reply_id' => Reply::select('id')
->whereHasMorph('repliable', ['App\Thread'], function ($q) {
$q->where('repliable_id', '=', 'threads.id');
})->latest('created_at')
->take(1),
]);
}
I have also tried
public function scopeWithRecentReply() {
return $query->addSelect([
'recent_reply_id' => Reply::select('id')
->where('repliable_id', '=', 'threads.id')
->latest('created_at')
->take(1),
]);
}
But in both cases the
recent_reply_id => null
If instead of threads.id i enter an integer, it works and the recent_reply_id is not null
For example
public function scopeWithRecentReply() {
return $query->addSelect([
'recent_reply_id' => Reply::select('id')
->whereHasMorph('repliable', ['App\Thread'], function ($q) {
$q->where('repliable_id', '=', 1);
})->latest('created_at')
->take(1),
]);
}
My question is
Is there a way to be able to fetch the recent_reply_id using the respective threads.id ?
I would suggest using appends instead of query scopes so in your Thread model we will add
protected $appends = ['recent_reply_id'];
.....
public function replies () {
// you need to add here your relation with replies table
}
public function getRecentReplyIdAttribute () {
return $this->replies()->latest('id')->first()->id;
}
now wherever you query the threads table you will have access to recent_reply_id like
$thread = Thread::where('id',1)->withRecentReply()->get();
$thread->recent_reply_id;
I have a query where I get values from 3 tables, for first 2 I use leftJoin, and is Ok, but for third one I try to get an array of objects, and I am not sure how.
In relationship table a have multiple rows for each ID from People table. HasMany type.
$q = Person::leftJoin('registers', 'people.register_id', '=', 'registers.id')
->leftJoin('relationships', 'people.id', '=', 'relationships.person_id') //if I comment this it works for first 2 tables
->find($id);
return response()->json($q);
Person
public function relationship()
{
return $this->hasMany(Relationship::class);
}
public function register()
{
return $this->belongsTo(Register::class);
}
Relationship
public function person()
{
return $this->belongsTo(Person::class, 'person_id');
}
Register
public function people(){
return $this->hasOne(Person::class);
}
UPDATE -> this works,but is kind of ugly, I think that should be a better way in Laravel
$q = Person::leftJoin('registers', 'people.register_id', '=', 'registers.id')
->find($id);
$q2 = Person::find($id)->relationship;
return response()->json([
'values' => $q,
'relationship' => $q2,
]);
You can just use with like this:
Person::with(['register', 'relationship'])->find($id);
Commodity:
public function variations() {
return $this->hasMany( CommodityVariation::class )->with( 'unit' );
}
Recipe:
public function commodity_variations() {
return $this->belongsToMany( CommodityVariation::class, 'recipe_commodity_variations' )->with( 'commodity' )->withTimestamps();
}
CommodityVariation:
public function commodity() {
return $this->belongsTo( Commodity::class )->with( 'variations', 'allergens' );
}
public function recipes() {
return $this->belongsToMany( Recipe::class, 'recipe_commodity_variations' )->withTimestamps();
}
What I'm looking for:
I would like to get all recipes that use/have this specific commodity, but I've to go through my commodity_variations to find the relation between recipe and commodity. This is because recipe is linked to commodity variations and commodity variations is linked to commodity.
So what I've build so far, is this, but it just returns 50 recipes regardleess of what commodity_id I enter. For instance if I write 100000 instead of $commodity->id this returns 50 recipes aswell.
$recipe = $department->recipes()->with(["commodity_variations" => function($q) use ( $commodity ) {
$q->where('commodity_variations.commodity_id', '=', $commodity->id);
}])->get();
I really can't figure out what I'm doing wrong. I might be misunderstanding some basic stuff on how these relations work.
Edit:
here is another example
$commodity_variations = $recipe->commodity_variations()->with( [
'commodity' => function ( $query ) use ( $commodity ) {
$query->where( 'commodities.id', $commodity->id );
}
] )->get();
Should return only the commodity variations that have commodity->id = 11, but it returns all. Like it's completely ignoring the where part of my query.
Try this once.
$commodity_variations = $recipe->whereHas('commodity_variations' ,
function ( $query ) use ( $commodity ) {
return $query->where( 'commodity_variations.commodity_id', $commodity->id );
}
)->get();
Also refer this: http://laravel.com/docs/4.2/eloquent#querying-relations
Here is a working solution. Made a new model taking care of the many to many relation between recipe and commodityvariation.
class RecipeCommodityVariations extends Model {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'recipe_commodity_variations';
}
Query:
$recipeCommodityVariations = RecipeCommodityVariations::whereIn( 'recipe_id', $department->recipes()->pluck( 'recipe_id' ) )->whereIn( 'commodity_variation_id', $commodity->variations()->pluck( 'id' ) )->pluck( 'recipe_id' )
What troubles me, is that I feel like I'm doing it wrong... It's pretty annoying.
I have relation like this:
public function message()
{
return $this->hasMany('Engine\Message');
}
inside my Conversation model.
and for each conversation I need to get last message.
Here is what I tried, but this will get only one message for first conversation but will not get message from other conversations...
$con = Conversation::all();
$con->load(['message' => function ($q) use ( &$mess ) {
$mess = $q->first();
}]);
return $con;
I don't wana query for each record... Anyone know how to solve this problem?
As suggested here!
Don't use first() or get() in eager loadings you should create a new relationship in the model.
The model would look something like this...
public function message()
{
return $this->hasOne('Engine\Message');
}
kudos to 'pmall'
Try
$con = Conversation::all();
$con->load(['message' => function ($q) use ( &$mess ) {
$q->orderBy('created_at', 'desc')->take(1);
// or if you don't use timestamps
// $q->orderBy('id', 'desc')->take(1)
}]);
return $con;