Laravel Eloquent map in query - php

I have a problem with mapping objects in a Laravel eloquent query.
How to map relations in a query builder.
How to connect two separate collections.
For example. Having models:
class CartProduct extends Model
{
protected $fillable = [
'quantity',
'cart_id',
'product_id',
'unit_price',
'product_code'
];
function product(){
return $this->belongsTo(Product::class);
}
function cart(){
return $this->belongsTo(Cart::class);
}
}
class Cart extends Model
{
function productsInCart()
{
return $this->hasMany(CartProduct::class);
}
public function products()
{
return $this->belongsToMany(
Product::class,
'cart_products',
'cart_id',
"product_id");
}
}
class Product extends Model
{
protected $fillable = [
'name',
'code',
'description',
'price',
];
}
The tasks are:
Get a set of products that are in the same cart (doesn't matter which one) as $product_id (excluding $product_id)
Get a set of products that were in any cart together with $product_id (excluding $product_id, without duplications)
I would solve it like this:
1.
public function task_one($product_id)
{
return $products = CartProduct::where('product_id', $product_id)->first()->cart->products
->filter(function (Product $p) use ($product_id) {
return $p->id !== $product_id;
});
}
public function task_two($product_id)
{
$cartProducts = CartProduct::where('product_id', $product_id)->get();
$products = collect(new Product);
foreach ($cartProducts as $cartProduct) {
$productsInCart = $cartProduct->cart->products
->filter(function (Product $p) use ($product_id) {
return $p->id !== $product_id;
});
$products = $products->merge($productsInCart);
}
return $products->unique();
}
However, the 2nd function seems to be awful. How can I do this properly, to achieve fast execution and a good style of code?
Is there any method to "map" the whole collection to related model objects? For example by
$carts = CartProduct::getByProductId($product_id)->"mapByRelationship('cart)";
//The result should be a collection od Carts
$products = CartProduct::getByProductId($product_id)->"mapByRelationship('cart)"->"mapByRelationship('products')"->unique();
//The result should be the same as task_two($product_id);
Thank you in advance

I think I have done this Controller-Model Relationship.
Controller:
class MakeAWishController extends Controller
{
public function getMakeAWishes(Request $request)
{
$limit = (int) ($request->limit ?? 1);
$offset = (int) ($limit * (($request->page ?? 1) - 1));
$wishes = MakeAWish::with('product')
->offset($offset)->limit($limit)->where('product_quantity', '>' , '0')->get()
->map(function ($wish) {
$wish->children_image = asset(Storage::url($wish->children_image));
if(!empty($variant = $wish->product->variant())) {
$wish->product->variant_id = $variant->variant_id;
$wish->product->variant_price = $variant->variant_price ?? "0.00";
$wish->product->variant_compare_at_price = $variant->variant_compare_at_price ?? "0.00";
}
return $wish;
});
$response = [
'status' => 200,
'data' => $wishes
];
return response()->json($response);
}
}
Model:
class MakeAWish extends Model
{
protected $collection = 'make_a_wishes';
protected $fillable = [
'children_name',
'children_name_for_isis',
'age',
'country',
'children_image',
'product_id',
'quantity'
];
protected $casts = [
'product_id' => 'string'
];
public function product()
{
return $this->hasOne(Product::class, 'product_id', 'product_id');
}
public function orders()
{
return $this->hasMany(OrderHistory::class, 'type_id', '_id');
}
public function orderCount()
{
return $this->orders()->where('type', 'M')->count();
}
}

Related

Configuration observer in Laravel

I am beginner in Laravel. I use in my project Laravel 8.
I have this code:
Controller
public function index(Request $request)
{
$query = $this->model
->orderBy($request->column ?? 'created_at', $request->order ?? 'desc');
if ($request->search) {
$query->where(function ($query) use ($request) {
$query->where('name', 'like', '%' . $request->search . '%')
->orWhere('id', 'like', '%' . $request->search . '%');
});
}
return DictionaryResource::collection($query->paginate($request->per_page));
}
public function create()
{
$statuses = DB::table('status')->select('status.name as label', 'status.id as value')->get();
$types = DB::table('dictionary_types')->select('dictionary_types.name as label', 'dictionary_types.id as value')->get();
return response()->json([$statuses, $types]);
}
public function store(DictionaryRequest $request)
{
$data = $request->only([
'name',
]);
if($request->status == 2) $status = 2;
else $status = 1;
if(is_null($request->type)) $type = 1;
else $type = $request->type;
$data['status'] = $status;
$data['type'] = $type;
$this->model->create($data);
return response()->json(['status' => 'success']);
}
Model
class Dictionary extends Model
{
use ScopeActiveTrait,
SoftDeletes;
protected $guarded = ['id'];
protected $fillable = [
'name',
'type',
'status'
];
protected $dates = [
'deleted_at',
'created_at',
'updated_at'
];
}
Observer
class DictionaryObserver
{
public function created(Dictionary $dictionary)
{
Log::info('yyyyyyyyy');
}
public function retrieved(Dictionary $dictionary)
{
Log::info('xxxxxxxxxx'.$dictionary);
}
public function updated(Dictionary $dictionary)
{
//
}
public function deleted(Dictionary $dictionary)
{
//
}
}
ServiceProvider
public function boot()
{
Paginator::useBootstrap();
Dictionary::observe(DictionaryObserver::class);
}
I have 2 questions / problems:
How can I disable following in the controller (index method)? I only need to record the moment when someone opens one record for editing, and does not list all the records in the list
I have model Action:
class Action extends Model
{
use ScopeActiveTrait,
SoftDeletes;
protected $guarded = ['id'];
protected $fillable = [
'company_id',
'user_id',
'ip',
'user_agent',
'description'
];
protected $dates = [
'deleted_at',
'created_at',
'updated_at'
];
}
I need save to this model information about user ip, user_agent itp (user is logged).
How can I make it?
As you've found, the "retrieved" method on the observer is called when you load the model instance, whether you load one or many (if you load many, it is called once for each model loaded).
You can suppress events being fired (and, having tested it, this includes both Events and Observers) by wrapping it in a callback function using the ::withoutEvents() static method.
So (using code from one of my sites) if I use :
$games = Game::where('id', '>=', 4900)->where('id', '<=', 4910)->get();
then the GameObserver will be called 11 times (because there are 11 models which are loaded). But if I wrap it in the ::withoutEvents method like so :
$games = Game::withoutEvents(function () {
$games = Game::where('id', '>=', 4900)->where('id', '<=', 4910)->get();
return $games;
});

Laravel find() does not get data from the DB

I have a resource Controller with this index method like this:
public function index()
{
$args = [];
$args = array_merge($args, $this->data_creator(35, 12, 'book'));
$args = array_merge($args, $this->data_creator(37, 12, 'kit'));
$args = array_merge($args, $this->data_creator(38, 12, 'game'));
$args['menu_links'] = [
'books' => route('shopping-products.category', Category::find(25)->slug),
'videos' => route('shopping-products.category', Category::find(24)->slug),
'kits' => route('shopping-products.category', Category::find(23)->slug),
'games' => route('shopping-products.category', Category::find(22)->slug),
];
return view('frontend.shop.products.index', $args);
}
But it returns this error:
Trying to get property 'slug' of non-object
And when I dd(Category::find(25), Category::find(24), Category::find(23), Category::find(22)); I get NULL results.
Meaning that it can not find data with specified ids.
However there are 25 records stored at the categories table:
So what is going wrong here? How can I fix this issue?
I would really appreciate any idea or suggestion from you guys...
Thanks in advance.
Here is Category.php Model:
class Category extends Model
{
use Sluggable, SoftDeletes;
protected $table = 'categories';
protected $primaryKey = 'cat_id';
protected $guarded = [];
/**
* Return the sluggable configuration array for this model.
*
* #return array
*/
public function sluggable()
{
return [
'slug' => [
'source' => 'cat_name'
]
];
}
public function path()
{
return "/products/categories/$this->slug";
}
public function children()
{
return $this->hasMany(Category::class, 'cat_parent_id', 'cat_id');
}
public function parents()
{
return $this->hasMany(Category::class, 'cat_id', 'cat_parent_id');
}
public function products()
{
return $this->belongsToMany(Product::class, 'category_products', 'ctp_cat_id', 'ctp_prd_id');
}
public function news()
{
return $this->belongsToMany(News::class, 'category_news', 'ctn_cat_id', 'ctn_nws_id');
}
public function galleries()
{
return $this->belongsToMany(Gallery::class, 'category_galleries', 'ctg_cat_id', 'ctg_gly_id');
}
public function uploaded()
{
return $this->hasMany(UploadedFile::class, 'upf_object_id', 'cat_id')->where('upf_object_type_id', '=', '107');
}
public function articles()
{
return $this->belongsToMany(Article::class, 'article_category', 'act_cat_id', 'act_art_id');
}
public function olympiadExam()
{
return $this->belongsToMany(OlympiadExam::class, 'olympiads_exams_categories', 'oec_ole_id', 'oec_cat_id');
}
public function olympiadExamQuestion()
{
return $this->belongsToMany(OlympiadExamQuestion::class, 'olympiads_exams_questions_categories', 'oes_cat_id', 'oes_oeq_id')->orderBy('oeq_number', 'asc');
}
public function attr_attributes()
{
return $this->hasMany(CategoryAttribute::class, 'category_id', 'cat_id');
} //
public function attr_product()
{
return $this->hasMany(Product::class, 'prd_cat_att_id', 'cat_id');
} //
public function couponRelation()
{
return $this->hasMany(couponRelation::class, 'object_id', 'cat_id')->where('object_type', 'product_category');
}
public function magazines()
{
return $this->belongsToMany(Magazine::class, 'category_magazine', 'category_id', 'magazine_id');
}
}
And when I do: dd(Category::where('cat_id', 25), Category::where('cat_id', 24), Category::where('cat_id', 23), Category::where('cat_id', 22)); I get this as result:
The problem is because you are using SoftDeletes so soft deleted models will automatically be excluded from query results. In your case, look like Category with id 22, 23, 24, 25 are soft deleted. To get it, you need to use withTrashed() as mentioned in the doc. For example:
Category::withTrashed()->find(22)->slug
per an answer above: if you are using soft deletes you need to add
Category::withTrashed()
However, you can wrap the command in an optional() helper function.
optional(Category::find(22))->slug
// if you are using soft delete
optional( Category::withTrashed()->find(22) )->slug
this will return null if 22 does not exist instead of throwing an exception error.

Saving array of dynamic fields data to DB using oneToMany relationship

I have been trying to save the data coming from the dynamically generated fields in the form of an array. I have a oneToMany relationship for the customer table.
I have tried to loop through each field but I am unable to achieve it, please correct me if I am wrong.
public function store(Request $request)
{
$res = $request->all();
$res['address'] = implode(' ', array_values($request->address));
$customer = Customer::create($res);
if ($res) {
$customerData = [];
foreach ($request->department_name as $key => $n) {
$customerData = array(
'department_name' => $request->department_name[$key],
'person_name' => $request->person_name[$key],
'person_number' => $request->person_number[$key],
'person_email' => $request->person_email[$key],
'notification_flag' => !isset($request->notification_flag[$key]) ? 0 : $request->notification_flag[$key] === "on" ? 1 : 0,
'custinvoice_noti' => !isset($request->outstanding[$key]) ? 0 : $request->outstanding[$key] === "on" ? 1 : 0,
'invoice_noti' => !isset($request->invoice[$key]) ? 0 : $request->invoice[$key] === "on" ? 1 : 0,
);
$deptModel[] = new Department($customerData);
$customer->department()->saveMany($deptModel);
}
}
return redirect('admin/customers');
}
Customer model and Department model have the following relationship.
class Customer extends Model
{
protected $fillable = ['owner_name', 'address', 'country', 'state', 'city', 'pincode', 'number', 'correspondance_check'];
public function department()
{
return $this->hasMany('App\Department');
}
}
Department Model.
class Department extends Model
{
protected $fillable = ['customer_id', 'department_name', 'person_name', 'person_number', 'person_email', 'notification_flag', 'notification_type'];
public function customer()
{
return $this->belongsTo('App\Customer');
}
}
public function store(Request $request)
{
$customer = new Customer();
$customer->owner_name = $request['owner_name'];
$customer->country = $request['country'];
$customer->state = $request['state'];
$customer->city = $request['city'];
$customer->pincode = $request['pincode'];
$customer->number = $request['number'];
$customer->correspondance_check = $request['correspondance_check'];
$res = $customer->save();
$cus = Customer::where(['id'=> $res->id])->firstOrFail();
$dep = new Department();
$dep->customer()->associate($cus);
$dep->save();
return redirect('admin/customers');
// return response()->json($customerData);
}
Customer model and Department model have the following relationship.
class Customer extends Model
{
protected $fillable = ['owner_name', 'address', 'country', 'state', 'city', 'pincode', 'number', 'correspondance_check'];
public function department()
{
return $this->hasMany('App\Department');
}
}
//Department Model.
class Department extends Model
{
protected $fillable = ['customer_id', 'department_name', 'person_name', 'person_number', 'person_email', 'notification_flag', 'notification_type'];
public function customer()
{
return $this->belongsTo('App\Customer');
}
}

Save data into two tables at same time in laravel

I am trying to save datas into two different tables depends on selection(x-editable). Here is my code and Help me by pointing where i am doing mistake.
Result i am looking for: I change payment status pending to paid in TBL:Product, paid values also change 0 to 1 in TBL:Product_payment
TBL:Product
- product_id
- client_id
...
- status {paid/pending}
TBL:Product_payment
- product_id
- payment_id
....
- paid {1/0}
Controller:
public function update()
{
$inputs = Input::all();
if ($row = Product::with('payments')->find($inputs['pk']))
{
$row->$inputs['name'] = $inputs['value'];
if($row['status'] == 'paid') {
$row['paid'] = 1;
}
$row->save();
return $row;
}
Product.php(model)
class Product extends Eloquent
{
protected $primaryKey = 'product_id';
protected $table = 'products';
protected $fillable = array('client_id', 'date', 'amount', 'status', 'notes');
public function payments()
{
return $this->hasMany('ProductPayment');
}
}
ProductPayment.php(model)
class ProductPayment extends Eloquent
{
public $table = 'product_payments';
protected $primaryKey = 'product_payment_id';
protected $fillable = array('product_id', 'client_id', 'payment_method', 'amount_paid', 'paid');
public function product()
{
return $this->belongsTo('Products');
}
public function clients()
{
return $this->belongsTo('Clients');
}
}
Add a model event listener to boot in your AppServiceProvider for whenever an instance of Product is saved.
Product::saved(function ($product) {
$paymentStatus = [
'pending' => 0,
'paid' => 1,
];
if(array_key_exists($product->status, $paymentStatus))
{
ProductPayment::where('product_id', $product->id)
->update(['paid' => $paymentStatus[$product->status]]);
}
});

Use eloquent relation on models returning by another method

I have a Category model which has belongsToMany relation with Product model via a pivot table called product_to_category
I can get all products in a Category with $category->products() and then apply a Filter scope to it to filter the result with parameters given in Request like this:
When I send this request :
http://site.dev/category/205?product&available&brand
I apply the parameters like this:
Category::find($id)->products()->filter($request)
The problem is when I want to get all product in a category and its children. The existing products relation gives me products in only given category.
I tried to modify the products() method in Category model as this:
public function products()
{
return DB::table('oc_product')
->join('oc_product_to_category', 'oc_product_to_category.category_id', '=', 'oc_product_to_category.category_id')
->join('oc_category_path', 'oc_category_path.category_id', '=', 'oc_category.category_id')
->whereIn('oc_product_to_category.category_id', $this->children(true));
}
But when I this code :
Category::find($id)->products()->filter($request)
I get this exception error:
(1/1) BadMethodCallException
Call to undefined method Illuminate\Database\Query\Builder::filter()
I know that filter scope is defined in Model class, but how can I apply that filter scope to QueryBuilder which is returned by modified products method?
Here are my classes :
Product model:
class Product extends Model {
public function scopeFilter( $request, QueryFilter $filters ) {
return $filters->apply( $request );
}
public function categories() {
return $this->belongsToMany( Category::class, 'product_to_category', 'product_id', 'category_id' );
}
}
Category model:
class Category extends Model
{
public function scopeFilter($query, QueryFilter $filters)
{
return $filters->apply($query);
}
public function children($id_only = false)
{
$ids = $this->hasMany(CategoryPath::class, 'path_id', 'category_id')
->join('category', 'category.category_id', '=', 'category_path.category_id')
->where('category.status', 1)
->pluck('category.category_id');
if ($id_only)
return $ids;
return self::find($ids);
}
public function parent()
{
$parent = DB::Select("SELECT cp.path_id AS category_id FROM category_path cp LEFT JOIN category_description cd1
ON (cp.path_id = cd1.category_id AND cp.category_id != cp.path_id)
WHERE cd1.language_id = '2' AND cp.category_id = " . $this->category_id);
return $parent;
}
public function products()
{
return $this->belongsToMany(Product::class, 'product_to_category', 'category_id', 'product_id');
}
}
QueryFilter class:
abstract class QueryFilter {
protected $request;
protected $builder;
public function __construct( Request $request ) {
$this->request = $request;
}
public function filters() {
return $this->request->all();
}
public function apply( Builder $builder ) {
$this->builder = $builder;
foreach ( $this->filters() as $name => $value) {
if (method_exists($this, $name)) {
call_user_func_array([$this, $name], array_filter([$value]));
}
}
return $this->builder;
}
}
CategoryFilter class:
class CategoryFilters extends QueryFilter
{
public function id($id)
{
return $this->builder->where('category_id', $id);
}
public function procons()
{
return $this->builder->with('pros', 'cons');
}
public function available()
{
return $this->builder->where('quantity', '>', 0);
}
public function optionValues()
{
return $this->builder->with('optionValues');
}
public function description()
{
return $this->builder->with('description');
}
public function images()
{
return $this->builder->with('images');
}
public function order($order)
{
$params = explode(',', $order);
$order = isset($params[0]) ? $params[0] : null;
$way = isset($params[1]) && strtolower($params[1]) == 'desc' ? $params[1] : 'asc';
if ($order) {
return $this->builder->orderBy($order, $way);
}
return $this->builder;
}
}

Categories