Convert Eloquent HasMany relationship to a HasOne? - eloquent

Ok, i have model A that is linked to many model B, so model A have a hasMany relationship to model B and model B has a belongsTo relationship to model A.
But, amongst all those B there is a particular one, e.g. the one with the higher value, that i want model A to have a dedicated relationship with.
I know i can create a second relationship in model A that re-use the old one and add all the scopes and condition i want, but since i'm building this new relationship from a hasMany, it will return always, no matter what, a collection of results.
Is there a way to have it return a single result instead than a collection? I know i can add a first(), but this involve in using this new relationship as a function, i.e. always with parenthesis, and if possible i would like to still use it the eloquent way.
Is it possible to do that? if yes, how?

How about this:
public function Bs()
{
return $this->hasMany(B::class);
}
public function B()
{
$builder = $this->Bs()->latest(); // Add your own conditions etc...
$relation = new HasOne($builder->getQuery(), $this, 'a_id', 'id');
return $relation; // or return $relation->withDefault();
}
Update:
A new one-of-many relationship shipped with Laravel 8.42. You probably want to use this instead:
public function B(): HasOne
{
return $this->hasOne(B::class)->latestOfMany();
}

Not really no. You have 2 models mapping tables with some attributes as follow
+-----+-----+
| A | B |
+-----+-----+
| id | id |
| ... | a_id|
| | ... |
+-----+-----+
When you call onto the hasMany relationship, the builder executes a query. Something like
SELECT * FROM `B` WHERE `B`.`a_id` = ?
If you make a different relationship for it, you will only duplicate the query (if you load both relationships). You could make an accessor, or just use first().
EDIT Accessor code:
# A model
public function getXAttribute()
{
// Query only if necessary
if(!this->relationLoaded('b')) $this->load('b');
// Use Collection methods to filter
return $this->b->firstWhere(...);
}

Related

Entity Framework - To sum reference object properties

I have the following models which represent my table relationship.
public class A { List<B> B_List }
public class B { public int amount }
I was wondering if it is possible to get top 5 class A objects order by "amount".
I was hoping there is a way to write an EF query to avoid pulling all A objects from database.
I think #Bilal's answer is close, but may not get you exactly what you want. By the way, you haven't been clear on what you mean by "top 5 A objects order by Amount". Each A object has multiple amounts. Are you wanting the top 5 A objects based on the maximum amount? If so, then try this ...
dbContext.AList
.OrderByDescending(a => a.B_List.Sum(b => b.amount))
.Take(5);

Select custom columns from Laravel belongsToMany relation

Im trying to select only specific attributes on the many-to-many relation users, just like in one-to-one. But using select() on belongsToMany() seem to be ignored and i'm still getting all the User attributes.
class Computer extends Eloquent {
public function users() {
return $this->belongsToMany("User")->select("email");
}
public function admin() {
return $this->hasOne("User")->select("email");
}
}
Computer::with("users")->get();
Is there a way of filtering only specified columns from related entity with belongsToMany()?
Yes, you actually can.
Computer::with("users")->get(array('column_name1','column_name2',...));
Be careful though if you have the same column name for both tables linked by your pivot table. In this case, you need to specify the table name in dot notation, tableName.columnName. For example if both users and computer has a column name id, you need to do :
Computer::with("users")->get(array('users.id','column_name2',...));
According to Taylor Otwell it is not currently possible: https://github.com/laravel/laravel/issues/2679
I have tried to use a lists('user.email') at the end of the query but I can't make it work.
Computer::with(["users" => function($query){
$query->select('column1','column2','...');
}])->get();

Entity Framework TPH - Additional WHERE clause only for one subtype

Suppose I have a class Parent, with two subclasses, AChild and BChild. I have these mapped to a single table using Entity Framework 5.0.0 on .NET 4.5, using TPH.
public abstract class Parent {
public string Type { get; set; } // Column with this name in DB is discriminator.
public string Status { get; set; }
}
public class AChild : Parent {
// Other stuff.
}
public class BChild : Parent {
// Other stuff.
}
The code to configure the mapping:
class ParentConfiguration : EntityTypeConfiguration<Parent> {
Map((EntityMappingConfiguration<AChild> mapping) => mapping
.Requires("Type")
.HasValue("A"));
Map((EntityMappingConfiguration<BChild> mapping) => mapping
.Requires("Type")
.HasValue("B"));
}
I have a need to run a query that returns both AChild and BChild objects. However, it needs to filter ONLY the AChild rows by a second column, which in this example I will call Status.
Ideally I would want to do the following:
public IList<Parent> RunQuery() {
IQueryable<Parent> query =
this.context.Set<Parent>()
.Where((Parent parent) => !parent.Type.Equals("A") || parent.Status.Equals("Foo"))
.OrderBy((Parent parent) => parent.Number);
return query.ToList();
}
This doesn't work. It insisted on looking for a "Type1" column instead of just letting both the discriminator and a "Type" property be mapped to the same "Type" column.
I know of the "OfType" extension method that can be used to completely filter down to one type, but that's too broad a brush in this case.
I could possibly run multiple queries and combine the results, but the actual system I'm building is doing paging, so if I need to pull back 10 rows, it gets messy (and inefficient) to query since I'll either end up pulling back too many rows, or not pull back enough and have to run extra queries.
Does anyone have any other thoughts?
There are few problems. First of all you cannot have discriminator mapped as a property. That is the reason why it is looking for Type1 column - your Type property results in second column because the first one is already mapped to .NET types of your classes. The only way to filter derived types is through OfType.
The query you want to build will be probably quite complex because you need to query for all Bs and concatenate them with result of query for filtered As. It will most probably not allow you to concatenate instances of Bs with As so you will have to convert them back to parent type.

How to populate a property that is not directly stored in Database with CodeFirst

I have 2 Entities, each of which is managed by EF Code First, and each happily sitting in its own table. Entity_A has a property, called "SumTotal", which should be the sum of a specific column in Entity_B that matches a certain criteria.
SumTotal should not be persisted in the DB, but rather calculated each time an instance of Entity_A is retrieved.
I have looked at ComputedColumns, but it appears that the computedcolumn can only be defined relative to columns in the same table.
I also have a feeling that I need to set SumTotal to NotMapped (or something similar with AutoGenerated), but dont know how to get the actual value into SumTotal.
Hope this question makes sense, thanks in advance
You can project the results to an anonymous object and transform that it to your entity
var projection = db.EntityAs.Where(/* */)
.Select(a => new {A = a, Sum = a.Bs.Sum(b => b.Total)})
foreach(p in projection)
{
p.A.SumTotal = p.Sum;
}
var As = projection.Select(p => p.A);

EF4 inheritance and Stored procedures

I implemented inheritance with a discriminator field so all my records are in the same table. My basetype is Person (also the name of the table) and Driver and Passenger inherit from it. I receive instances of the correct type (Driver and Passenger) when I perform a query on the object context to Person. example:
var q = from d in ctx.Person
select d;
But I also create a function that calls a stored procedure and mapped the output of the function to the type Person. But now I get a list of Person and not Drivers or Passengers when I execute this method.
Anybody an idea how to solve this or is this a bug in EF4?
AFAIK, you can't use discriminator mapping (e.g TPH) when dealing with stored procedure mappings.
The stored procedure must be mapped to a complex type or custom entity (e.g POCO), the mapping cannot be conditional.
What you could do is map it to a regular POCO, but then project that result set into the relevant derived type (manual discrimination).
E.g:
public ICollection<Person> GetPeople()
{
var results = ExecuteFunction<Person>(); // result is ObjectResult<Person>
ICollection<Person> people = new List<Person>();
foreach (var result in results)
{
if (result.FieldWhichIsYourDiscriminator == discriminatorForDriver)
{
people.Add((Driver)result);
}
// other discriminators
}
}
If your always expecting a collection of one type (e.g only Drivers), then you wouldn't need the foreach loop, you could just add the range. The above is in case you are expecting a mixed bag of different people types.
Would be interested to see other answers, and if there is a better way though - but the above should work.