Laravel eloquent conditional relationship - php

I am trying to set up a conditional relationship in eloquent model based on the value of table column, but it does not works.
This is the code that I am using:
//RELATIONS
public function task_schedule()
{
if ($this->task_schedule_id === 0) {
return $this->belongsTo('TaskSchedule', 'hold_task_schedule_id', 'id');
} else {
return $this->belongsTo('TaskSchedule');
}
}
Basically I want to use different column to get my child model defined in belongs to relationship.
scopeProfile($query) would not work for me because I dont want to retrieve the child in every single query and also I am using Task->with('task_schedule') in a lot parts of code.

Related

Laravel Eloquent - Get results from parents that has only one child

I'm trying to get all results that match a certain child id, and that the parent has only on child. My initial idea its to do it by hand with nested foreachs, but is there any method that does this in a simpler way?
My parent is called Proyectos and then I have a pivot table called PaisBeneficiarioProyecto, and the other parent is PaisBeneficiario. It's a many-to-many relation.
$monto_pais = Proyecto::all()->where('pais_beneficiarios', $id)->sum('monto_aprobado');
The code explained above does what you expect, but it's not the result that I need.
Here are my models:
Proyectos:
public function pais_beneficiarios()
{
return $this->belongsToMany(\App\Models\PaisBeneficiario::class, 'pais_beneficiario_proyecto', 'id_proyecto', 'id_pais_beneficiario')
->withPivot('monto_beneficio');
}
PaisBeneficiario:
public function pais_beneficiario()
{
return $this->belongsTo(\App\Models\PaisBeneficiario::class, 'id_pais_beneficiario');
}
public function proyecto()
{
return $this->belongsTo(\App\Models\Proyecto::class, 'id_proyecto');
}
PaisBeneficiarioProyectos:
public function proyectos()
{
return $this->belongsToMany(\App\Models\Proyecto::class, 'pais_beneficiario_proyecto', 'id_pais_beneficiario', 'id_proyecto')
->withPivot('monto_beneficio');
}
The value that I'm trying to sum is called monto_aprobado which is on Proyectos
The below query gives you the sum of monto_aprobado field of Proyecto models that jus have one pais_beneficiarios_count.
Proyecto::withCount('pais_beneficiarios')
->having('pais_beneficiarios_count', 1)
->sum('monto_aprobado');

Eloquent query not working

I have a many to many relationship between users and categories. In a third table, I have a field called category_id to reference what category each record belongs to. I am setting up a system where by once a user logs in, I want them to see records that has category_id of the categories they've selected when registering.
My code is shown below
I have this relationship setup in users model,
public function categories()
{
return $this->belongsToMany('App\Category');
}
and also this
public function userCats()
{
return $this->categories()->get();
}
in the categories table i have this relationship setup
public function users()
{
return $this->belongsToMany('App\User');
}
in my 3rd Table controller i have the following code
if(Auth::check()){
$servo = Jumong::postOnly()->whereIn('category_id', Auth::user()->userCats())->get();
} else {
$servo = Jumong::all()->where('type','P');
}
dd($servo);
The problem is that the below part,
Auth::user()->userCats()
It returns only the last 2 records in Jumong table and there are more than that.
If I replace it with an array, it will return the right results.
Any ideas on what I am missing?
It doesn't work because whereIn expects an array of ids whereas
Auth::user()->userCats() returns a collection of categories.
You can still doing something like.
Auth::user()->categories->pluck('id')

how to get a belongsToMany() query from a collection ? MySQL/Laravel

I'm using Laravel 5.4.22 (the newest one). In MySQL, I have two tables, tag_categories and tags, which form a many-to-many relationship. What I need is a query which returns all the tags for the selected categories. I know how to solve this when I have only one object, and I know how to solve this with querying and looping each of those objects, but there has to be a query or eloquent based solution for the whole thing?
I understand the code below doesn't work because I'm using ->belongsToMany on a collection rather than an object, but how to I bridge this gap the simplest way?
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)
->belongsToMany(Tag::Class)->get();
dd($resultingTags);
belongsToMany generally belongs in the model class, not a method called on the fly. When looking to eager load the relationship, you then call the with() method on the query builder.
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
ex:
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
// Query
$users = User::with('roles')->get();
$rolesOfFirstUser = $users->first()->roles;
If you're trying to get all the tags of the given categories, then you should be querying tags, not tag_categories.
Tag::whereHas('categories', function ($query) use ($chosenCategoriesIds) {
$query->whereIn('id', $chosenCategoriesIds);
})->get();
This is One-to-many relation
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->hasMany('App\Tag');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();
If you want define Many-To-Many relation for this case
You need to have 3 tables tags, tag_categories, tag_tag_category
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->belongsToMany('App\Tag', 'tag_tag_category', 'tagcategory_id', 'tag_id');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();

Laravel 5.3 Polymorphic many to many - how to get all the related rows regardless of their table sorted by pivot column?

I have a model Page and many models called SomethingSection - they're connected through a polymorphic m-m realtionship and the pivot has an additional column 'position'.
I need to write a relationship (or accessor maybe?) on the Page model that will return a collection of all connected Sections, regardless of their model (read: table).
My models:
class Page extends Model {
public function introSections()
{
return $this->morphedByMany(IntroSection::class, 'pagable');
}
public function anotherSections()
{
return $this->morphedByMany(AnotherSection::class, 'pagable');
}
}
class IntroSection extends Model {
public function pages()
{
return $this->morphToMany(Page::class, 'pagable');
}
}
class AnotherSection extends Model {
public function pages()
{
return $this->morphToMany(Page::class, 'pagable');
}
}
The pivot column looks like this:
pagables
-page_id
-pagable_id
-pagable_type
-position
I'm looking for a way to call a method/attribute on the Page model and get all the connected sections in a single collection, sorted too. What would be a good way to go about this?
I understand that the connected sections do not have the same interface, but in my case that's not a problem at all (in terms of what I will do with the data).
I also understand that relationships perform a separate query (for each relationship), so getting all of them with 1 query is impossible (also different interfaces would be a problem here). And for the same reason the sorting will need to be done on the collection level, not in query.
How could I make this as maintainable as possible and preferably with as small a performance hit as possible.
Thanks in advance.
You can use withPivot() method after your relationship to get the pivot columns with relation like this:
class Page extends Model {
public function introSections()
{
return $this->morphedByMany(\HIT\Models\Sections\IntroSection::class, 'pagable')
->withPivot(['position']);
}
public function anotherSections()
{
return $this->morphedByMany(AnotherSection::class, 'pagable');
}
}
class IntroSection extends Model {
public function pages()
{
return $this->morphToMany(Page::class, 'pagable')
->withPivot(['position']);
}
}
and you can use collection's sortBy to sort the collection by using sortBy() method like this:
$sorted_collection = IntroSection::pages->sortBy('pagables.position');
UPDATE:
You can use collection's combine() method to get all the relationships like this, add this method inside your Page Class:
public function getAllSections()
{
return $this->introSections->combine($this->anotherSections-toArray())
->sortBy('pagables.position'):
}
Hope this helps!

Laravel orWhere not working with hasMany

I have following code:
class Ingredient extends Eloquent
{
public function units()
{
return $this->hasMany('IngredientUnit')
->orWhere('ingredient_id', '=', -1);
}
}
I would expect query like:
select * from `ingredient_units` where `ingredient_id` = '-1' OR `ingredient_units`.`ingredient_id` in (...)
instead I get:
select * from `ingredient_units` where `ingredient_id` = '-1' and `ingredient_units`.`ingredient_id` in (...)
Why it use AND operator instead OR, when I used orWhere()?
Update 1:
And second question is how can I get a query which I was expected?
Update 2:
I want to use eagerloading for that
When you fetch a collection of objects through a relation on a model, the relation constraint is always included, hence the AND. And it makes perfect sense, otherwise you could get $model->units objects that are not related to $model.
I can see what you're trying to achieve here - fetch units related to that $model together with units not related to any models. You can achieve it by adding the following method to your model:
public function getAllUnits() {
$genericUnits = IngredientUnit::whereIngredientId(-1);
return $this->units()->union($genericUnits)->get();
}
OR
public function getAllUnits() {
return IngredientUnit::whereIn('ingredient_id', [$this->id, -1])->get();
}
The only issue here is that it won't be used by eager loading logic but would result in separate query for every model for which you want to return units. But if you always fetch that for a single model anyway, it won't be a problem.
One suggestion: store NULL in ingredient_id instead of -1. This way you'll be able to make use of foreign key constraints.

Categories