Get Parent from Child in Laravel without extra query - php

I am trying to make a small system with two models: Product, ProductPrice.
Here is the Product model:
class Product extends Model
{
protected $with = ['prices'];
public $tax_rate = 0.2;
public function prices ()
{
return $this->hasMany(ProductPrice::class);
}
}
I put the tax_rate constant here for more clarity, but in real world, it is handled by another relation.
The most important thing here is that the tax_rate is a property of the Product model
Here is the ProductPrice model:
class ProductPrice extends Model
{
protected $appends = ['tax_included_price'];
public function getTaxIncludedPriceAttribute()
{
return (1 + $this->product->tax_rate) * $this->price;
}
public function product ()
{
return $this->belongsTo(Product::class);
}
}
Now let's imagine that I need to use $product->toArray() on some model. With this example, I will get an Exception for infinite loop because my getTaxIncludedPriceAttribute() method makes a new request to find the product attribute.
So could I access the Product parent in the ProductPrice model if I access to it through the parent, and without making an extra query

So, I solved the problem with a handmade solution, not sure of implementation, but it works like I want it to work.
class Product extends Model
{
protected $with = ['pricesRelation'];
protected $appends = ['prices'];
public $tax_rate = 0.2;
public function pricesRelation ()
{
return $this->hasMany(ProductPrice::class);
}
public function getPricesAttribute ()
{
$collection = new Collection();
foreach($this->pricesRelation as $relation) {
$relation->loadProduct($this);
$collection->add($relation);
}
return $relation;
}
}
As you see, I run a $relation->loadProduct($this); to define parent on relation without re querying it...
class ProductPrice extends Model
{
protected $appends = ['tax_included_price'];
protected $loaded_product;
public function getTaxIncludedPriceAttribute()
{
$tax_rate = is_null($loaded_product) ? $this->product->tax_rate : $this->loaded_product->tax_rate;
return (1 + $tax_rate) * $this->price;
}
public function loadProduct (Product $product)
{
$this->loaded_product = $product;
}
public function product ()
{
return $this->belongsTo(Product::class);
}
}

Related

Laravel safest way to delete a many to many relationship data while sync (replace) with another

I have a project with Product, Category relationship which is Many to Many
// Product Model
public function categories()
{
return $this->belongsToMany(Category::class);
}
//Category Model
public function products()
{
return $this->belongsToMany(Product::class);
}
Now, when a some category gets deleted, I want to assign it's products to a default category (ID = 1).
What is the best way to achieve this with Laravel 8
Finally I found the solution thanks to Kevin
public static function boot()
{
parent::boot();
static::deleting(function ($category) {
$products = $category->products;
if ($products->isNotEmpty()) {
$category->products()->detach();
$defaultCategory = static::find(1);
$defaultCategory->products()->sync($products->pluck('id')->toArray());
}
});
}
You might want to try the deleting event:
class Category extends Model
{
public static function booted()
{
static::deleting(function ($category) {
$products = $category->products()->get();
if ($products->isNotEmpty()) {
$category->products()->detach();
$defaultCategory = static::find(1);
$defaultCategory->products()->sync($products->pluck('id')->toArray());
}
})
}
}

How to get only active element from many to many relationship with translation in laravel

I have a problem with a many to many relationship and the translations of the terms.
I have 4 tables:
products
- id, price, whatever
products_lang
- id, product_id, lang, product_name
accessori
- id, active
accessori_lang
- id, accessori_id, lang, accessori_name
I'm trying to assign accessories to products with an intermediate table named:
accessori_products
this is the model for Product:
class Product extends Model {
protected $table = 'products';
public function productsLang () {
return $this->hasMany('App\ProductLng', 'products_id')->where('lang','=',App::getLocale());
}
public function productsLangAll() {
return $this->hasMany('App\ProductLng', 'products_id');
}
public function accessori() {
return $this->belongsToMany('App\Accessori', 'accessori_products');
}
}
this is the model for productLng:
class ProductLng extends Model {
protected $table = 'products_lng';
public function products() {
return $this->belongsTo('App\Product', 'products_id', 'id');
}
}
Then I have the model for Accessori:
class Accessori extends Model {
protected $table = 'accessori';
public function accessoriLang() {
return $this->hasMany('App\AccessoriLng')->where('lang','=',App::getLocale());
}
public function accessoriLangAll() {
return $this->hasMany('App\AccessoriLng');
}
public function accessoriProducts() {
return $this->belongsToMany('App\Products', 'accessori_products', 'accessori_id', 'products_id');
}
}
And the model for AccessoriLng:
class accessoriLng extends Model {
protected $table = 'accessori_lng';
public function accessori() {
return $this->belongsTo('App\Accessori', 'accessori_id', 'id');
}
}
I get the results by this:
$products = Product::has('accessori')->with([
'productsLang ',
'accessori' => function ($accessori){
$accessori->with([
'accessoriLang'
]);
}
])->get();
return $products;
but I want to get only the active accessories something like where accessori.active = 1 but I really don't know where to put it. I've tried in different way but I'm stuck on it by 2 days.
IIRC you don't need a model for the intermediate table on your many to many relationships.
If you want to return Products where Accessori is active you can use whereHas on the Product model.
$prod = Product::whereHas('accessori', function($query) {
$query->where('active', 1);
})->get();
Where the $query param will be running on the Accessori model.
You can do the inverse as well with Accessori to Product.
$acessoris = Accessori::where('active', 1)->whereHas('accessoriProduct')->with(['accessoriLang', 'accessoriProducts.productsLang'])->get();

Getting all products from catalog laravel

I have 3 tables:
product
*id
category_id
name
...
category
*id
catalog_id
name
...
catalog
*id
name
...
and 3 models
class Catalog extends Model
{
public function category()
{
return $this->hasMany('App\Category');
}
}
class Category extends Model
{
public function product()
{
return $this->hasMany('App\Product');
}
public function catalog()
{
return $this->belongsTo('App\Catalog');
}
}
class Product extends Model
{
public function category()
{
return $this->belongsTo('App\Category');
}
}
I'm working with data through repositories
example:
abstract class Repository
{
protected $model = false;
public function get($select = '*',$where = false, $take = false)
{
$builder = $this->model->select($select);
if($where)
{
$builder->where($where);
}
if($take)
{
$builder->take($take);
}
return $builder->get();
}
}
class ProductsRepository extends Repository
{
public function __construct(Product $products)
{
$this->model = $products;
}
public function getNewProducts()
{
return $this->get('*',['is_recommended' => 1],Config::get('settings.recommended_products_count'));
}
public function getProductsFromCategory($category_id)
{
return $this->get('*',['category_id' => $category_id]);
}
}
So, the question is: how can I get all products from catalog by it's id?
in raw sql it'll look like:
select * from products
join categories
on categories.id=products.category_id
join catalogs
on catalogs.id=categories.catalog_id
where(catalogs.id=1)
but how can I get them in my situation?
First, define a relation between catalogue and products in Catalog model:
public function products()
{
return $this->hasManyThrough('App\Product', 'App\Category', 'catalog_id', 'category_id', 'id');
}
Now, you should be able to get all products for given catalog with:
$products = Catalog::with('products')->find($id)->products;
You can find out more about has-many-through relation here: https://laravel.com/docs/5.4/eloquent-relationships#has-many-through

Laravel 4 one to many Relationship insertion

I'm using Laravel 4. I have a table called shipment and it related with many Items.
Here my code
Shipment controll
class ShipmentControl extends BaseController{
public function init(){
$places=Places::lists('place','id');
return View::make('Shipment.shipmentadd')
->with('places',$places);
}
public function save(){
//echo "Im HERE";
//$shipment=Input::all();
$rules=array(
'initials'=>'required',
'surname'=>'required',
'rece_initials'=>'required',
'rece_surname'=>'required',
'email'=>'email',
'rece_email'=>'email',
'nort_email'=>'email',
);
$v=Validator::make(Input::all(),$rules);
if($v->fails()){
return Redirect::to('shipment')->withInput()->withErrors($v);
}
$shipment=new Shipment;
$shipment->initials=Input::get('initials',null);
$shipment->surname=Input::get('surname',null);
$shipment->addrsss=Input::get('address',null);
$shipment->email=Input::get('email',null);
$shipment->telephone=Input::get('telephone',null);
$shipment->reciver_initials=Input::get('rece_initials',null);
$shipment->reciver_surname=Input::get('rece_initials',null);
$shipment->reciver_address=Input::get('rece_address',null);
$shipment->reciver_email=Input::get('rece_email',null);
$shipment->reciver_telephone=Input::get('rece_telephone',null);
$shipment->notfy_name=Input::get('nort_name',null);
$shipment->notify_mail=Input::get('nort_email',null);
$shipment->notify_telephone=Input::get('nort_telephone',null);
$shipment->port_of_loading=Input::get('port_of_loading',null);
$shipment->vessle=Input::get('vessel',null);
$shipment->eta=Input::get('eta',null);
$shipment->place_of_collection=Input::get('place',null);
if($shipment->save()){
$items=Session::get('items');
foreach ($items as $value) {
# code...
//array_unshift($value,$shipment->id());
$Items=new Items;
$Items->shipment_id=$shipment->id();
$Items->type=$value['type'];
$Items->qty=$value['qty'];
$Items->height=$value['height'];
$Items->weight=$value['width'];
$Items->lenght=$value['length'];
$Items->save();
}
}
Session::flush();
}
}
Shipment Model
lass Shipment extends Eloquent{
protected $table = 'shipment';
public $timestamps = false;
public function places(){
return $this->belongsTo('Places','place_of_collection');
}
public function items(){
return $this->hasMany('items','shipment_id');
}
}
and Here Item Model
class Items extends Eloquent{
protected $table = 'items';
public $timestamps = false;
public function shipment(){
return $this->belongsTo('Shipment','shipment_id');
}
}
I'm Using a Session to keep items. So when I try to insert details It show me this error
Call to undefined method Illuminate\Database\Query\Builder::id()
But the shipping details are added to the table.I googled it but i can't find the Solution. What is this Build::id is..?

Laravel 4 - foreign key constraint fails

I have the following relations:
Discount:
<?php
class Discount extends Eloquent {
protected $table = 'discount';
public $timestamps = true;
public function title()
{
return $this->hasOne('Translation', 'labelId', 'titleLabelId')->where('languageId', T::getLang())->first()['phrase'];
}
public function titles()
{
return $this->hasMany('Translation', 'labelId', 'titleLabelId');
}
}
?>
Translation:
<?php
class Translation extends Eloquent {
protected $table = 'translations';
public $timestamps = false;
protected $fillable = array('phrase', 'languageId', 'labelId');
public function language()
{
return $this->belongsTo('Language', 'languageId');
}
public function label()
{
return $this->belongsTo('Label', 'labelId');
}
}
?>
Label:
<?php
class Label extends Eloquent {
protected $table = 'label';
public $timestamps = false;
protected $fillable = array('key');
public function translations()
{
return $this->hasMany('Translation', 'labelId', 'id');
}
}
?>
There are three database tables with the following columns:
Discount:
id | titleLabelId
Translation:
id | languageId | labelId
Label:
id
The problem: I'd like to create a title (translation) and associate it with the discount. Here's what I've tried:
$discount = new Discount;
/*create a new label*/
$labelKey = Label::max('key') + 1;
$label = new Label(array('key' => $labelKey));
$label->save();
/*create a new title (and associate it with the label)*/
$title = new Translation(
array(
'phrase' => $input['title'],
'languageId' => 3,
'labelId' => $label->id
));
$title->save();
$discount->save();
$discount->titles()->save($title);
Apparently, the $discount->titles()->save($title); part doesn't work. The title is only attached to the discount if I do it manually: $discount->titleLabelId = $label->id. Is there a way to do it using the ORM?
In your Discount Model, do you have your relationship set up to use the proper table and foreign key?
class Discount extends Eloquent
{
public function titles()
{
return $this->belongsTo('Translation', 'translations', 'titleLabelId');
}
}
When trying to associate one model with another through a defined relationship in Eloquent, you should use the associate() method rather than the save() method.
$discount->titles()->associate($title);
Before this happens though, you should be sure to call the save() method on anything that has been altered or is new.

Categories