AutoMapper expand children of expanded entity - entity-framework

Given the following code
class Program
{
static void AutoMap()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<PERSON, Pessoa>()
.ForMember(x => x.PessoaId, y => y.MapFrom(z => z.PERSON_ID))
.ForMember(x => x.PessoaName, y => y.MapFrom(z => z.PERSON_NAME))
.ForMember(x => x.Coisas, y => { y.MapFrom(z => z.ITEMs); y.ExplicitExpansion(); })
.ReverseMap()
.ForMember(x => x.ITEMs, y => { y.MapFrom(z => z.Coisas); y.ExplicitExpansion(); });
cfg.CreateMap<ITEM, Coisa>()
.ForMember(x => x.CoisaId, y => y.MapFrom(z => z.ITEM_ID))
.ForMember(x => x.CoisaName, y => y.MapFrom(z => z.ITEM_NAME))
.ForMember(x => x.Pessoa, y => { y.MapFrom(z => z.PERSON); y.ExplicitExpansion(); })
.ForMember(x => x.Propriedades, y => { y.MapFrom(z => z.PROPERTies); y.ExplicitExpansion(); })
.ReverseMap()
.ForMember(x => x.PERSON, y => { y.MapFrom(z => z.Pessoa); y.ExplicitExpansion(); })
.ForMember(x => x.PROPERTies, y => { y.MapFrom(z => z.Propriedades); y.ExplicitExpansion(); });
cfg.CreateMap<PROPERTY, Propriedade>()
.ForMember(x => x.PropriedadeId, y => y.MapFrom(z => z.PROPERTY_ID))
.ForMember(x => x.PropriedadeName, y => y.MapFrom(z => z.PROPERTY_NAME))
.ForMember(x => x.Coisa, y => { y.MapFrom(z => z.ITEM); y.ExplicitExpansion(); })
.ReverseMap()
.ForMember(x => x.ITEM, y => { y.MapFrom(z => z.Coisa); y.ExplicitExpansion(); });
});
}
static void Main(string[] args)
{
AutoMap();
RicardoDataContext context = new RicardoDataContext();
IQueryable<Pessoa> pessoas = context.PERSONs.ProjectTo<Pessoa>(x => x.Coisas);
foreach (Pessoa pessoa in pessoas)
{
Console.WriteLine(pessoa.PessoaId + ": " + pessoa.PessoaName);
if (pessoa.Coisas != null)
{
foreach (Coisa coisa in pessoa.Coisas)
{
Console.WriteLine("\t" + coisa.CoisaId + ": " + coisa.CoisaName);
if (coisa.Propriedades != null)
{
foreach (Propriedade propriedade in coisa.Propriedades)
{
Console.WriteLine("\t\t" + propriedade.PropriedadeId + ": " + propriedade.PropriedadeName);
}
}
}
}
}
return;
}
}
How can I invoke AutoMapper's ProjectTo() so that Propriedades (children of Coisas who in turn are the children of Pessoa) so that they are also expanded (and I will get them in the same SQL query such as right now with PERSON left outer join ITEM - would expect another left outer join on ITEM->PROPERTY)?

Related

How to use filter result with list integer in IQueryable

My program contains several tables. Some of these tables are as follows:
Country table - Product table - ProductCountry (junction table).
When filters are applied on the page of the store (example.com/shop), I want to apply the filter of the selected countries to the result, which goes through various stages (the ID of these countries is a list of integer)
I wrote several ways, all of which have errors.
Thank you for your help
IQueryable<ProductLocalization> result = _context.ProductLocalizations
.Include(c => c.Languages)
.Include(c => c.ProductPropertyLocalizations)
.Include(c => c.Product)
.Include(c => c.Product.UserProducts)
.Include(c => c.Product.Brand)
.Include(c => c.Product.Category)
.Include(c => c.Product.Category.CategoryPropertyLocalizations)
.Where(c => c.LanguageId == languageId);
if (countryId.Count != 0)
{
result = result.Where(c => countryId.Contains(c.Product.CountryProducts.Select(b => b.CountryId));
result = result.Where(c => c.Product.CountryProducts.Any(b => b.CountryId == countryId));
result = result.Where(c => countryId.Any(b => b == c.Product.CountryProducts[0].CountryId)); //for example
result = result.Where(c => c.Product.CountryProducts.Select(c => countryId.Any(c.CountryId)));
result = result.Where(c => c.Product.CountryProducts.FindAll(b => b.CountryId == countryId));
result = result.Where(c => c.Product.CountryProducts.Select(c => c.CountryId).Contains(countryId);
result = result.Where(c => countryId.Any(b => b == c.Product.CountryProducts.Select(a=>a.CountryId)));
}
Try the following filter:
result = result.Where(c =>
c.Product.CountryProducts.Any(b => countryId.Contains(b.CountryId))
);

is it possible to return different models in an API response

This is my first time posting here so please pardon my errors:
I have a search functionality whose route is:
Route::get('/search', 'SearchController#index');
Currently, I have an eloquent relationship where products has many deals. is it possible to return a single level deep array doing the following:
If the product has an active deal, return the deal only;
Otherwise, return the product itself.
here's what I earlier implemented in my Product.php:
public function deals()
{
return $this->hasMany(Deal::class, 'product_id');
}
Deal.php
public function product()
{
return $this->hasOne(Product::class, 'id', 'product_id');
}
SearchController:
public function index(Request $request)
{
$per_page = $request->per_page ?? 10;
$products = Product::query()->latest()
->when($request->query('filter'), function ($query) use ($request) {
$query->with('deals')->where('title', 'LIKE', "%$request->filter%");
})
->when($request->query('category'), function ($query) use ($request) {
$query->with('deals')->whereHas('categories', function ($q) use ($request) {
$q->where('title', 'LIKE', "%$request->category%");
});
})
->paginate($per_page);
return new PaginatedCollection($products, ProductResource::class);
}
and in my ProductResource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
public function toArray($request)
{
$details = array_filter($this->details ?: [], function ($d) {
return $d != "";
});
$personalizedOptions = array_filter($this->personalized_options ?: [], function ($o) {
return $o != "";
});
return [
'id' => $this->id,
'createdAt' => $this->created_at,
'updatedAt' => $this->updated_at,
'title' => $this->title,
'sellerId' => $this->sellerId,
'description' => $this->description,
'categories' => CategoryResource::collection($this->categories),
'details' => $details,
'active' => (bool) $this->active,
'defaultPreviewImageId' => $this->default_preview_image_id,
'originalPrice' => $this->originalPrice,
'shippingPrice' => $this->shippingPrice,
'shippingWeightLbs' => $this->shippingWeightLbs,
'shippingWeightOz' => $this->shippingWeightOz,
'shippingMaxDays' => $this->shipping_max_days,
'shippingMinDays' => $this->shipping_min_days,
'personalized' => (bool) $this->personalized,
'personalizedOptions' => $personalizedOptions,
'deals' => $this->deals ?? null,
'options' => ProductOptionResource::collection($this->productOptions),
'images' => ImageResource::collection($this->images->whereNull('meta')),
'preview' => new ImageResource($this->images->where('meta', '=', 'preview')->first()),
];
}
}
Now, I have refactored the ProductResource to this but it's all returning null response
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
public function toArray($request)
{
$details = array_filter($this->details ?: [], function ($d) {
return $d != "";
});
$personalizedOptions = array_filter($this->personalized_options ?: [], function ($o) {
return $o != "";
});
if($this->deals){
DealResource::collection($this->deals);
}else{
return [
'id' => $this->id,
'createdAt' => $this->created_at,
'updatedAt' => $this->updated_at,
'title' => $this->title,
'sellerId' => $this->sellerId,
'description' => $this->description,
'categories' => CategoryResource::collection($this->categories),
'details' => $details,
'active' => (bool) $this->active,
'defaultPreviewImageId' => $this->default_preview_image_id,
'originalPrice' => $this->originalPrice,
'shippingPrice' => $this->shippingPrice,
'shippingWeightLbs' => $this->shippingWeightLbs,
'shippingWeightOz' => $this->shippingWeightOz,
'shippingMaxDays' => $this->shipping_max_days,
'shippingMinDays' => $this->shipping_min_days,
'personalized' => (bool) $this->personalized,
'personalizedOptions' => $personalizedOptions,
// 'deals' => $this->deals ?? null,
'options' => ProductOptionResource::collection($this->productOptions),
'images' => ImageResource::collection($this->images->whereNull('meta')),
'preview' => new ImageResource($this->images->where('meta', '=', 'preview')->first()),
];
}
}
}
The reason why it may be giving the null result because of the condition check. it is returning an array you need to update it to this.
if(count($this->deals))
this will check if the deal array contains an element in the array. if not it will return products.

Optimal sampling from the database

I make a sample around the city, the city has areas, they have complexes, and apartment complexes. In the end, I need to get an apartment for a certain city, indicating the associated filters (district, complex, etc.). At the moment I did this:
public function actionIndex()
{
$cities = Cities::find()->where(['id' => Yii::$app->request->get('city_id')])->with([
'districts' => function ($query){
$query->filterWhere([
'id' => Yii::$app->request->get('district_id'),
]);
},
'districts.complexes' => function ($query) {
$query->filterWhere([
'id' => Yii::$app->request->get('complex_id'),
'type_id' => Yii::$app->request->get('complex_type_id'),
'developer_id' => Yii::$app->request->get('developer_id'),
]);
},
'districts.complexes.apartments' => function ($query) {
$query->filterWhere([
'amount_room' => Yii::$app->request->get('amount_room'),
'yardage' => Yii::$app->request->get('yardage'),
'level' => Yii::$app->request->get('level'),
'price' => Yii::$app->request->get('price'),
]);
},
])->all();
$query = [];
foreach ($cities as $city) {
foreach ($city->districts as $district) {
foreach ($district->complexes as $complex) {
foreach ($complex->apartments as $apartment) {
$query[] = $apartment;
}
}
}
}
return new ArrayDataProvider([
'allModels' => $query,
]);
}
But it looks kind of crooked, maybe I went the wrong way, and can I do it better?
UPD: I did almost like Yasin Patel.
$cities = Cities::find()
//->select('cities.id') // list your attributes comma saperated
->leftJoin('districts','cities.id=districts.city_id') // join districts table
->leftJoin('complex','districts.id=complex.district_id') // join complex table
->leftJoin('apartment','complex.id=apartment.complex_id') // join apartment table
->where(['cities.id' => Yii::$app->request->get('city_id')])
->andWhere(['cities.id' => Yii::$app->request->get('city_id')])
->filterWhere(['districts.id' => Yii::$app->request->get('district_id')])
->filterWhere(['complex.id' => Yii::$app->request->get('complex_id')])
->filterWhere(['complex.type_id' => Yii::$app->request->get('complex_type_id')])
->filterWhere(['complex.developer_id' => Yii::$app->request->get('developer_id')])
->filterWhere(['apartment.amount_room' => Yii::$app->request->get('amount_room')])
->filterWhere(['apartment.yardage' => Yii::$app->request->get('yardage')])
->filterWhere(['apartment.level' => Yii::$app->request->get('level')])
->filterWhere(['apartment.price' => Yii::$app->request->get('price')]);
return new ActiveDataProvider([
'query' => $cities,
]);
Returns:
[
{
"name":"City1",
"region":{
"id":7,
"name":"Region city1."
}
}
]
But how now to choose all the apartments found?
Try this query.
$cities = Cities::find()
->select('city.id') // list your attributes comma saperated
->leftJoin('districts','city.id=districts.city_id') // join districts table
->leftJoin('complexes','districts.id=complexes.districts_id') // join complexes table
->leftJoin('apartments','complexes.id=apartments.complexe_id') // join apartments table
->where(['id' => Yii::$app->request->get('city_id')])
->andWhere(['city.id' => Yii::$app->request->get('district_id')])
->andWhere(['districts.id' => Yii::$app->request->get('district_id')])
->andWhere(['complexes.id' => Yii::$app->request->get('complex_id')])
->andWhere(['complexes.type_id' => Yii::$app->request->get('complex_type_id')])
->andWhere(['complexes.developer_id' => Yii::$app->request->get('developer_id')])
->andWhere(['apartments.amount_room' => Yii::$app->request->get('amount_room')])
->andWhere(['apartments.yardage' => Yii::$app->request->get('yardage')])
->andWhere(['apartments.level' => Yii::$app->request->get('level')])
->andWhere(['apartments.price' => Yii::$app->request->get('price')])
->asArray();
Use your table name and attributes names as needed.
Than use this query in ArrayDataProvider.
return new ArrayDataProvider([
'allModels' => $query,
]);
This will return you records as array.
It turned out to optimize the code in this way::
public function actionIndex()
{
$query = Apartment::find()
->joinWith('complex')
->joinWith('complex.district')
->joinWith('complex.district.city')
->where(['cities.id' => Yii::$app->request->get('city_id')])
->filterWhere(['districts.id' => Yii::$app->request->get('district_id')])
->filterWhere(['complex.id' => Yii::$app->request->get('complex_id')])
->filterWhere(['complex.type_id' => Yii::$app->request->get('complex_type_id')])
->filterWhere(['complex.developer_id' => Yii::$app->request->get('developer_id')])
->filterWhere(['apartment.amount_room' => Yii::$app->request->get('amount_room')])
->filterWhere(['apartment.yardage' => Yii::$app->request->get('yardage')])
->filterWhere(['apartment.level' => Yii::$app->request->get('level')])
->filterWhere(['apartment.price' => Yii::$app->request->get('price')]);
return new ActiveDataProvider([
'query' => $query,
]);
}
Thanks to everyone for their help.

EF CodeFirst Mapping relationship between derived classes to the same table

I need to do the following mapping
OrderItem will map to tbl_order_item
OrderItemCustomization will map to tbl_order_item_customization
I have a column [cd_order_item] in tbl_order_item_customization which is the foreign key
The problem is how I map the relationship between
CardOrderItem -> CardOrderItemCustomization
CustomCardOrderItem -> CustomCardOrderItemCustomization
I tried this
public class OrderItemConfiguration : EntityTypeConfiguration<OrderItem>
{
public OrderItemConfiguration()
{
ToTable("tbl_order_item");
HasKey(i => i.Id);
Property(i => i.Id).HasColumnName("cd_order_items");
HasRequired(i => i.Order).WithMany(o => o.Items).Map(m => m.MapKey("cd_order"));
HasRequired(i => i.Product).WithMany().Map(m => m.MapKey("cd_product"));
Property(i => i.Quantity).HasColumnName("cd_quantity");
Property(i => i.UnitAmount).HasColumnName("vl_item").HasColumnType("money");
Property(i => i.TotalAmount).HasColumnName("vl_total").HasColumnType("money");
Property(i => i.DiscountAmount).HasColumnName("vl_discount");
Property(i => i.CostAmount).HasColumnName("vl_cost_price");
Property(i => i.NetAmount).HasColumnName("vl_net");
HasRequired(i => i.Status).WithMany().Map(m => m.MapKey("cd_status"));
Property(i => i.IsActive).HasColumnName("fl_active");
}
}
public class AbstractCardOrderItemConfiguration : EntityTypeConfiguration<AbstractCardOrderItem>
{
public AbstractCardOrderItemConfiguration()
{
Property(i => i.IsVirtual).HasColumnName("fl_virtual");
Property(i => i.CardType).HasColumnName("cd_card_type").IsOptional();
Property(i => i.Document).HasColumnName("nr_document");
HasRequired(i => i.Package).WithRequiredPrincipal().Map(m => m.MapKey("cd_order_item_base"));
}
}
public class CardOrderItemConfiguration : EntityTypeConfiguration<CardOrderItem>
{
public CardOrderItemConfiguration()
{
HasRequired(i => i.CardOrderItemCustomization).WithRequiredPrincipal().Map(m => m.MapKey("cd_order_items"));
}
}
public class CustomCardOrderItemConfiguration : EntityTypeConfiguration<CustomCardOrderItem>
{
public CustomCardOrderItemConfiguration()
{
HasRequired(i => i.CardWithCustomPhotoOrderItemCustomization).WithRequiredPrincipal().Map(m => m.MapKey("cd_order_items"));
}
}
But this gives me an error:
One or more validation errors were detected during model generation:
cd_order_items: Name: Each property name in a type must be unique.
Property name 'cd_order_items' is already defined.
There is any way that can I achieve the desired mapping?

symfony 1.4 validation form bind problem

I have some problem with validation forms in symfony 1.4
Here is code:
class NormalForm extends sfForm
{
public function configure()
{
$this->setWidgets(array(
'name' => new sfWidgetFormInput(),
'phone' => new sfWidgetFormInput(),
'terms' => new sfWidgetFormInputCheckbox(array('value_attribute_value' => 1, 'default' => true))
));
$this->widgetSchema->setNameFormat('results[%s]');
$this->setValidators(array(
'name' => new sfValidatorString(array('required' => true), array('required' => 'This field may not by empty')),
'phone' => new sfValidatorNumber(array('min' => 100000000), array('required' => 'This field may not by empty','min' => 'To short','invalid' => 'Not a number')),
'terms' => new sfValidatorBoolean(array('required' => true), array('required' => 'Accept the regulations'))
));
}
}
class homeActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$this->nform = new NormalForm();
$this->module_action = "/".$this->moduleName."/".$this->actionName;
$this->name = $request->getPostParameter('results[name]');
$this->phone = $request->getPostParameter('results[phone]');
$this->foo = $request->getPostParameter('results');
if($request->isMethod('post')){
$this->message .= 'post ';
$this->nform->bind($request->getParameter('results'));
if($this->nform->isValid()){
$this->message .= 'is valid';
$this->save = 'save';
}
}
}
}
This is here: http://nowa.hipotekaplus.pl
When form is valid then should this text show:
<h1>Zgłoszenie zarejestrowano 1</h1>
<p>Dziękujemy za przesłanie zgłoszenia.
Twój osobisty doradca na telefon z pewnością zadzwoni w ciągu 24 godzin (w dni robocze).
Zgłoszenia obsługują doradcy kredytowi na telefon Hipotekaplus.pl.</p>
Can somebody tell me why this not work ?