ASP.NET Core : getting values ​if its id exists in relational table with EF6 or Linq? - linq

I have a date table and a time table. These two tables are related. In the date table, I created the date and defined times for this date. I want to get the list of dates for which the time is specified from the database.
This is my query:
public async Task<List<ReserveDate>> GetReservedates()
{
return await _context.ReserveDates
.Include(t => t.ReserveTimes)
.OrderBy(r => r.DateReserve)
.ToListAsync();
}

If I understand question correctly, you need filter by existence of ReserveTimes:
public async Task<List<ReserveDate>> GetReservedates()
{
return await _context.ReserveDates
.Include(t => t.ReserveTimes)
.Where(t => t.ReserveTimes.Any())
.OrderBy(r => r.DateReserve)
.ToListAsync();
}

Related

How do I return the ID field in a related table in Laravel request

I have two related tables and I want to return all fields including the ID (key) field. My query below returns all fields except the ID. how do I return the ID field from one of the tables?
'programmes' => ProgrammeInstances::with('programmes')->get(),
the query below returns Unknown column 'programmes.programme_title' as it is looking for it in the table 'programme_instances'
'programmes' => ProgrammeInstances::with('programmes')->select('programmes.programme_title', 'programmeInstances.id', 'programmeInstances.name', 'programmeInstances.year')->get(),
Laravel provides multiple relationships, one of these is the hasMany() relationship which should return a collection where a User hasMany rows inside of your database
For example, inside your User model :
public function programmes() {
return $this->hasMany(Program::class);
}
Now in your controller, you can do :
public function edit($id) {
$programmes = User::find($id)->with('programmes')->get();
return view('user.edit')->with('programmes', $programmes);
}
And then you can loop over it inside your view
#forelse($programmes->programmes as $program)
// provide the data
#empty
// the user doesn’t have any programmes
#endforelse
a solution i found below - still not sure why ID isnt automatically returned when i get all fields, but works when i specify individual fields:
'programmes' => ProgrammeInstances::with('programmes')
->get()
->transform(fn ($prog) => [
'programme_title' => $prog->programmes->programme_title,
'id' => $prog->id,
'name' => $prog->name,
'year' => $prog->year,
]),

Problem with data sorting, sortBy function

Sorry, maybe dumb question, but I have problem with sorting result, I just want sort result by updated_at->data, descending, so the newest records on top
public function boostsRequest()
{
$boosts = Cache::remember('boosts_all', 30, function () {
$raw = Boost::where('status', 3)->where('hidden', false)->with('screenshot')->get();
$raw->transform(function ($boost) {
return [
'currentdivision' => $boost->currentdivision,
'desireddivision' => $boost->desireddivision,
'wins' => $boost->wins,
'type' => $boost->type,
'screenshot' => optional($boost->screenshot)->url,
'updated_at' => $boost->updated_at,
];
});
$raw = $raw->sortBy(function ($boost) {
return strtotime($boost->updated_at->date);
});
return $raw;
});
return $boosts;
}
Normally, the result of a function call is a JSON table filled with objects with the following structure:
{"currentdivision":"12","desireddivision":"16","wins":null,"type":"division","screenshot":"https:\/\/ucarecdn.com\/86c6d345-1a7f-4af4-8005-175ee235ccd8\/","updated_at":{"date":"2016-11-03 11:11:26.000000","timezone_type":3,"timezone":"UTC"}}
I want to have a table of objects sorted by the field updated_at-> date, descending, so recently updated objects as first
the updated_at value is not an object, just delete the ->date part, only this code is needed.
return strtotime($boost->updated_at);
tip: sometimes when we doubt about how a value is represented or how a php function behave on a table record, we can use tinker :D.
Just sort the collection by updated time in the eloquent request:
$raw = Boost::where('status', 3)->where('hidden', false)->with('screenshot')->orderBy('updated_at')->get();
Of the updated_at value, you are sure it exists in the database. No closure needed.

ActiveRecord where and order on via-table

I have three database table:
product (id, name)
product_has_adv (product,advantage,sort,important)
advantage (id, text)
In ProductModel I defined this:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id']);
}
I get the advantages without any problems.
But now I need to add a where product_has_advantage.important = 1 clausel and also sort the advantages by the sort-columen in the product_has_advantage-table.
How and where I have to realize it?
Using via and viaTable methods with relations will cause two separate queries.
You can specify callable in third parameter like this:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'], function ($query) {
/* #var $query \yii\db\ActiveQuery */
$query->andWhere(['important' => 1])
->orderBy(['sort' => SORT_DESC]);
});
}
The filter by important will be applied, but the sort won't since it happens in first query. As a result the order of ids in IN statement will be changed.
Depending on your database logic maybe it's better to move important and sort columns to advantage table.
Then just add condition and sort to the existing method chain:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'])
->andWhere(['important' => 1])
->orderBy(['sort' => SORT_DESC]);
}
Using viaTable methods with relations will cause two separate queries, but if you don't need link() method you can use innerJoin in the following way to sort by product_has_advantage table:
public function getAdvantages()
{
$query = AdvantageModel::find();
$query->multiple = true;
$query->innerJoin('product_has_advantage','product_has_advantage.advantage = advantage.id');
$query->andWhere(['product_has_advantage.product' => $this->id, 'product_has_advantage.important' => 1]);
$query->orderBy(['product_has_advantage.sort' => SORT_DESC]);
return $query;
}
Note than $query->multiple = true allows you to use this method as Yii2 hasMany relation.
Just for reference https://github.com/yiisoft/yii2/issues/10174
It's near impossible to ORDER BY viaTable() columns.
For Yii 2.0.7 it returns set of ID's from viaTable() query,
and final/top query IN() clause ignores the order.
For who comes here after a while and don't like above solutions, I got it working by joining back to the via table after the filter via table.
Example for above code:
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'])
->innerJoin('product_has_advantage','XXX')
->orderBy('product_has_advantage.YYY'=> SORT_ASC);
}
Take care about changing XXX with the right join path and YYY with the right sort column.
First you need to create a model named ProductHasAdv for junction table (product_has_adv) using CRUD.
Then create relation in product model and sort it:
public function getAdvRels()
{
return $this->hasMany(ProductHasAdv::className(), ['product' => 'id'])->
orderBy(['sort' => SORT_ASC]);;
}
Then create second relationship like this:
public function getAdvantages()
{
$adv_ids = [];
foreach ($this->advRels as $adv_rel)
$adv_ids[] = $adv_rel->advantage;
return $this->hasMany(Advantage::className(), ['id' => 'advantage'])->viaTable('product_has_adv', ['product' => 'id'])->orderBy([new Expression('FIELD (id, ' . implode(',', $adv_ids) . ')')]);
}
This will sort final result using order by FIELD technique.
Don't forget to add:
use yii\db\Expression;
line to head.
I`ve managed this some how... but it needs additional work after.
The point is that you have to query many-to-many relation first from source model and after that inside that closure you should query your target model.
$query = Product::find();
$query->joinWith([
'product_has_adv' => function ($query)
{
$query->alias('pha');
$query->orderBy('pha.sort ASC');
$query->joinWith(['advantage ' => function ($query){
$query->select([
'a.id',
'a.text',
]);
$query->alias('a');
}]);
},
]);
Then you just have to prettify the sorted result to your needs.
The result for each row would look like
"product_has_adv": [
{
"product": "875",
"advantage": "true",
"sort": "0",
"important": "1",
"advantage ": {
"id": "875",
"text": "Some text..",
}
},
As explained by #arogachev, the viaTable uses two separate queries, which renders any intermediate orderBy obsolete
You could replace the viaTable with an innerJoin as follows, in a similar solution to #MartinM
public function getAdvantages()
{
return $this->hasMany(AdvantageModel::class, ['pha.product' => 'id'])
->innerJoin('product_has_advantage pha', 'pha.advantage = advantage.id')
->andWhere(['pha.important' => 1])
->orderBy(['pha.sort' => SORT_ASC]);
}
By adjusting the result of hasMany, you are adjusting the query for the target class - AdvantageModel::find(); product_has_advantage can be joined via the advantage identity
The second parameter of hasMany, link, can be viewed as [ query.column => $this->attribute ], which you can now support via the joined product_has_advantage and its product identity
Note, when using viaTable, the link parameter can be viewed as if the intermediate query is complete and we are starting from there; [ query.column => viaTable.column ]
hence ['id', 'advantage'] in your question
public function getAdvantages()
{
return $this
->hasMany(AdvantageModel::className(), ['id' => 'advantage'])
->viaTable('product_has_advantage', ['product' => 'id'])
->andWhere(['important' => 1])
->orderBy(['sort' => SORT_DESC]);
}

Query not projecting for oderby?

How can I add an orderby after the select?
//what I have now
string country_list = string.Join(":", ctx.Countries.Select(a => a.CountryName).ToArray());
return country_list;
//what I want to do, but the orderby doesnt see the projections
string country_list = string.Join(":", ctx.Countries.Select(a => a.CountryName).OrderBy(b => b.StateId).ToArray());
return country_list;
its the projection with b that isnt working
You have to call OrderBy before Select, because after projection the column you're trying to order by is no longer available:
string country_list = string.Join(":", ctx.Countries.OrderBy(b => b.StateId).Select(a => a.CountryName).ToArray());
ctx.Countries.Select(a => new { a.CountryName, a.StateId })
.OrderBy(b => b.StateId)
.ToArray()

Deeper level LINQ query in a lambda expression

I would like to get those employees who have a phone number == "666666" using a LINQ query.
These are the class definitions:
public class Employees
{
public List<Phones> Phones{get;set}
}
public class Phones
{
public string Id{get;set;}
public string Number{get;set;}
}
This is my query (my doubt indicated as ???):
var employees= data.GetEmployees()
.Where(e=> e.Phones ???i need to navigate a level below phones ???)
.Select(e => new Employee()
{
Id=e.Id,
Name=e.Name
});
My problem is I don't know how to go a deeper level in this LINQ expression, because in e=>e... I have access to Phones as an IEnumerable but I would like to navigate to Phone's properties.
The easiest way to do this is to use nested LINQ queries. In this case you should look at the Any method.
var employees= data
.GetEmployees()
.Where(e => e.Phones.Any(p => p.Number == "666666"))
.Select(e => new Employee() {
Id = e.Id,
Name = e.Name
});
The parameter passed to the where method is merely a function that returns true or false for each given element, all methods (including LINQ ones (subject to accessing ref/out params etc)) can still be called within it:
var employees= data.GetEmployees()
.Where(e => e.Phones.Any(p => p.Number == "666666"))
.Select(e => new Employee()
{
Id=e.Id,
Name=e.Name
});
var employees= data.GetEmployees()
.Where(e=> e.Phones.Contains(x=>x.Number == "666666"))
.Select(e => new Employee()
{
Id=e.Id,
Name=e.Name
});

Resources