Reduce stock number in laravel - laravel

How can I reduce the number of stock in laravel?
In my products table i have stock column where i set numbers of item i have and i want to reduce it in 2 conditions:
1- when is added to order table (not when the product is in user cart)
2- when the order status is not cancelled (if order status become cancelled stocks increase back)
PS: currently I have no code for this matter that's why i didn't share
any code, I'm seeking for idea of how done it. Base on that i will
make functions and share here to complete it.
JSON of Order
"[{\"id\":29,\"name\":\"effewf\",\"price\":24524,\"quantity\‌​":1,\"attributes\":[‌​{\"attr\":{\"label\"‌​:\"Gray\",\"price\":‌​\"7000.00\"}}],\"con‌​ditions\":[]},{\"id\‌​":27,\"name\":\"new product\",\"price\":7246,\"quantity\":2,\"attributes\":[],\"‌​conditions\":[]}]"
UPDATE
base on #btl answer I added code below in my order model:
protected static function boot()
{
parent::boot();
$productIdsAndQuantities = $this->products->map(function ($product) {
return [$product->id => $product->quantity];
});
static::updating(function (Order $order) {
// restock if cancelled
$oldStatus = OrderStatus::find($order->getOriginal('orderstatus_id'));
if ($oldStatus->title !== 'Cancelled' && $order->orderstatus->title === 'Cancelled') {
$order->products->each(function(Product $product) {
$stock = $product->getAttribute('stock');
$product->update([
'stock' => $stock + $product->order->getAttribute($productIdsAndQuantities)
]);
});
}
});
}
I think this code needs fix in $productIdsAndQuantities and $product->update(['stock' => $stock + $product->order->getAttribute($productIdsAndQuantities)]); parts before i can use it.
currently i get error below when i try to add my cart detail to orders table
Using $this when not in object context
UPDATE 2
I changed my code to:
protected static function boot()
{
parent::boot();
static::updating(function (Order $order) {
$productIdsAndQuantities = $order->product_name->map(function ($product) {
return [$product->id => $product->quantity];
});
// restock if cancelled
$oldStatus = OrderStatus::find($order->getOriginal('orderstatus_id'));
if ($oldStatus->title !== 'Cancelled' && $order->orderstatus->title === 'Cancelled') {
$order->products->each(function(Product $product) {
$stock = $product->getAttribute('stock');
$product->update([
'stock' => $stock + $product->order->getAttribute($productIdsAndQuantities)
]);
});
}
});
}
now my order will be placed but nothing changes on stock column.
any idea?

Your tables look like,
Product
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
......
$table->integer('stock');
$table->timestamps();
});
Stock
Schema::create('stocks', function (Blueprint $table) {
$table->increments('id');
$table->integer('product_id')->unsigned();
$table->integer('order_id')->unsigned();
$table->enum('fall_rise',[-1,1]);
$table->timestamps();
$table->foreign('product_id')->references('id')->on('products');
$table->foreign('order_id')->references('id')->on('orders');
});
orders
Schema::create('orders', function (Blueprint $table) {
$table->increments('id');
.....
.....
$table->timestamps();
});
Now, add a sample product with stock value 10 like
$product = new Product();
$product->name = "abc";
......
$product->stock = 10;
$product->save();
Now, when abc is ordered, add details in order table. Now, stock value to be decreased then update value of stock column in product by 10-1 = 9, add record in stocks table like
Sample values,
product_id = 1;
order_id = 1;
....
fall_rise = -1;
In this way, when 10 stocks of the product are ordered then stock column becomes zero and hence product becomes unavailable. If you want to fetch only available products then use query like,
$available_products = Product::where('stock', '>', 0)->get();
I hope you will understand.

Register model events to handle updates on orders.
In your Order model:
protected static function boot()
{
parent::boot();
static::updating(function (Order $order) {
// restock if cancelled
$oldStatus = OrderStatus::find($order->getOriginal('orderstatus_id');
if ($oldStatus->name !== 'cancelled' && $order->orderstatus->name === 'cancelled') {
$order->products->each(function(Product $product) {
$stock = $product->getAttribute('stock');
$product->update(['stock' => $stock + $product->order->getAttribute('quantity_of_product_ordered')]);
});
}
// added to order table similar to above...
// check if it's a new product id being added to the order, decrease stock quantity accordingly...
// ...
});
}

SOLVED
the trick was to use try { on my CartController when I wanted to save my data in orders table there I should place my codes like:
try {
$order = new Order();
$qty = Cart::getTotalQuantity();
$order->product_data = $cartItems;
$order->user_id = Auth::user()->id;
// rest of it...
Auth::user()->orders()->save($order);
//trick is in this part
foreach ($cartItems as $item) {
$product = Product::find($item->id);
$product->decrement('stock', $item->quantity);
}
the answer was provided here

Related

how to retrieve limited number of related model and sort collection by related model in laravel?

I have 3 model
Shop model, Product model, Picture model
I want to retrieve a collection of shops with last 3 Product model with their pictures and sort my collection based on newest product.
I tried leftjoint and joint in laravel 6 to be able to sort the result but i get all shops`product (i only need last 3 product for each shop),
when I use joint I cant retrieve product pictures
I also have tried “with” method in laravel , I couldnt sort the result based on product.creatred_at and also i get all related product in this method too.(as i mentioned i need the last 3 product)
class Shop extends Model
{
public function products()
{
return $this->hasMany('App\Product');
}
}
class Product extends Model
{
public function shop()
{
return $this->belongsTo('App\Shop');
}
public function pictures()
{
return $this->morphMany('App\hPicture', 'pictureable');
}
}
Shop::select('shops.*', 'products.id', 'products.shop_id', 'products.name as pname', 'products.user_id', 'products.code', 'products.price')
->with(['pictures', 'products.pictures'])
->leftjoin('products', function ($leftJoin) {
$leftJoin->on('shops.id', '=', 'products.shop_id');
});
$dataList = $dataList->orderBy($field, $order);
$dataList = $dataList->paginate(5)->appends(['sortField' => $field, 'sortOrder' => $order]);
the table layout for product and shop model is:
Schema::create('shops', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('slug');
$table->string('phone')->nullable();
$table->string('address')->nullable();
$table->timestamps();
$table->string('description')->nullable();
$table->uuid('uuid');
});
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->integer('shop_id')->unsigned();
$table->foreign('shop_id')->references('id')->on('shops');
$table->string('name');
$table->string('code');
$table->string('slug');
$table->integer('price');
$table->uuid('uuid');
$table->timestamps();
});
There are only 2 ways of resolving this:
Either you pull in all products, and trim them in the end(advisable only if not too many products per shop):
$shops = Shop::with(['products' => function($subQuery) {
$subQuery
->with('pictures') //Running in scope of product, also load each product's pictures
->orderBy('created_at', 'desc');
}])
->get();
foreach ($shops as $shop) {
$shop->setRelation('products', $shop->products->take(3));
}
NOTE:
You will load every single product that is linked to the shops you load. You could get memory issues with this.
Take only what you need, but introduce a n+1 query issue(advisable only with small quantities of $shops:
$shops = Shop::get();
foreach ($shops as $shop) {
$shop->load([
'products' => function($query) {
$query
->orderBy('created_at', 'desc')
->limit(3)
->get();
}]);
}
NOTE:
N+1 query problem: You are performing a new query for each shop, so if you have a million shops, it will be a million extra queries.
EDIT: (answering comment question)
Q: How can i sort $shops based on their latest product created_at field?
$sortedShops = $shops->sortBy(function ($shop, $key) {
return $shop->products->first()->created_at;
})->values()->all();
sortBy is called on the collection(not uery). It allows you to go over each element(in this case shops) and use each object. Please do note that this function will fail if you have no products linked to the shop.
The ->values()->all() at the end makes sure that when you convert your shops to json, you will create an array, and not an object in js.
Source:
https://laravel.com/docs/7.x/collections#method-sortby
EDIT: (deleted original answer as it did not work)
Previous answer does not work, because limit(3) will limit the total amound of products loaded, in stead of 3 products per shop(my bad).

Refactor 3 parent child category relation queries with whereHas or another type of query?

So I have categories and channels table with a following relationship. A category hasMany channels. What I'm trying to do is get all channels that belong to a parents sub categories. I have one working attempt (Attempt 2 in controller) at this time and am wondering if I could make that into one query?
Channel Categories
$table->increments('id');
$table->string('name');
$table->string('slug');
$table->integer('parent_id')->default(null);
Channels
$table->increments('id');
$table->string('name');
$table->string('slug');
$table->integer('category_id');
Get All Channels By Category Slug Route
Route::get('/{channelCategory}', 'ChannelController#index');
ChannelController
public function index($channelCategory)
{
//Attempt 1. This works perfectly fine, but would like it to be in one query if possible
/*if($channelCategory->parent_id === 0){
$categories = ChannelCategory::where(['parent_id' => $channelCategory->id])->pluck('id');
$channels = Channel::whereIn('category_id', $categories)->get();
} else {
$channels = $channelCategory->channels;
}*/
//Attempt 2 whereHas Query.
//The problem is that it gets all posts from all parent categories instead of just one.
/*$channels = Channel::whereHas('category', function ($query) use ($channelCategory) {
$query->where('parent_id', $channelCategory->parent_id);
$query->orWhere('parent_id', null);
})->get(); */
return view('channels.home', compact('channels'));
}
Maybe what I am trying to do isn't possible with a whereHas. Is it possible to do the second attempt in one query and if so how?
I think you can do this by just eager loading the channels then mapping the category channels together:
$categories = ChannelCategory::with('channels')
->where('parent_id', $channelCategory->id)
->get();
return view('channels.home', [
'channels' => $categories->flatMap->channels
]);
Pagination would likely need to be done manually using the LengthAwarePaginator class:
$page = $request->get('page', 1);
$perPage = $request->get('perPage', 15);
$channels = $categories->flatMap->channels;
$items = $channels->forPage($page, $perPage);
$paginator = new LengthAwarePaginator($items, $channels->count(), $perPage, $page);
Getting the latest would involve sorting the collection and taking the limit desired:
$limit = $request->get('limit', 10);
$latest = collect($categories->flatMap->channels)->sortByDesc('created_at')->take($limit);

Store foreign key value in laravel

In Migration Table:
Schema::create('passws', function (Blueprint $table) {
$table->increments('id');
$table->integer('regist_id')->unsigned()->nullable();
$table->foreign('regist_id')->references('id')->on('regists');
$table->string('age');
$table->timestamps();
});
}
Regist Model - Mass Assignment Defined.
public function pass(){
return $this->hasOne('App\Passw');
}
In Controller
$name = $request->name;
$task = Regist::whereName($name)->get();
foreach ($task as $tasks){
$passw1->regist_id = $tasks->id;
}
$passw1->age = $request->age;
$regist = new Regist();
$regist->pass()->save($passw1);
When i store data, only age is getting stored but regist_id stored as null, (no error message or nothing). Here i'm sure controller shows "regist_id" => 1 and "age" => 25 when i use dd($passw1); regist_id is only not getting stores in DB table.
Where am i doing an error??
Try this
$name = $request->name;
$regist = Regist::whereName($name)->first(); //it will give you a Regist model
$regist->pass()->create([
'age' => $request->age
]);
If you have more than one Regist for $name then try this
$regists = Regist::whereName($name)->get(); //it will give you a collection of Regist model
foreach ($regists as $regist){
$regist->pass()->create([
'age' => $request->age
]);
}
Check document here https://laravel.com/docs/5.6/eloquent-relationships#the-create-method

customer debt report laravel 5.3

I have laravel project with vouchers table
this is the structure of the vouchers table
Schema::create('vouchers', function (Blueprint $table) {
$table->increments('id');
$table->string('voucher_number',25)->unique();
$table->integer('voucher_dealer');
$table->date('voucher_date');
$table->integer('voucher_customer_id');
$table->double('voucher_amount');
$table->text('voucher_note');
$table->integer('voucher_type');
$table->integer('voucher_inserter');
$table->timestamps();
$table->softDeletes();
});
And the model name is Voucher
Now when i need to get customer voucher i use this relation
public function CustomerReportGet(Request $request)
{
$full = Voucher::where('voucher_customer_id','=',$request->customer_id)->sum('voucher_amount');
if($request->from_date == '' and $request->to_date == '')
$data = Voucher::where('voucher_customer_id','=',$request->customer_id)->get();
elseif($request->from_date <> '' or $request->to_date <> '')
{
$data = Voucher::where('voucher_customer_id','=',$request->customer_id)->
whereBetween('created_at',array($request->from_date,$request->to_date));
}
return array($full,$data);
}
And i have them as ajax request in the blade page
now my problem is that when i create the report
its should be like this ..
customer id
voucher date
voucher amount
customer balance till the voucher date
how can i let laravel always sum the voucher_amount till every voucher_date in the blade
thanks
Try the following:
public function CustomerReportGet(Request $request)
{
$full = Voucher::where('voucher_customer_id','=',$request->customer_id);
if($request->from_date == '' and $request->to_date == '')
$full->sum('voucher_amount')->get();
elseif($request->from_date <> '' or $request->to_date <> '')
{
$full->whereBetween('created_at',array($request->from_date,$request->to_date))->sum('voucher_amount')->get();
}
return array($full,$data);
}

laravel 4 group collection/results

I am trying to do a simple group by 'alpha' in my loop for a glossary page but not sure how to achieve this. My data is
alpha glossary_title
A Apple B Banana A Another Apple!
GlossaryController.php
public function index() {
$glossary = Glossary::groupBy('alpha')->orderBy('glossary_title', 'asc')->get();
return View::make('glossary')->with('glossary', $glossary);
}
migration
Schema::create('glossary', function(Blueprint $table)
{
// columns
$table->increments('id');
$table->string('glossary_title');
$table->text('glossary_desc')->nullable();
$table->char('alpha', 1);
$table->timestamps();
});
glossary.blade.php
#foreach($glossary as $glossary)
<li>{{ $glossary->glossary_title}} <br /> {{ $glossary->glossary_desc}}</li>
#endforeach
This will return a new collection groupped by $glossary->alpha:
$glossary = Glossary::orderBy('glossary_title', 'asc')->get();
$glossary = $glossary->groupBy('alpha');
// same as this:
$glossary = $glossary->groupBy(function ($entry) {
return $entry->alpha;
});
You will need 2 loops in your view then, first for groups, second for entries in each group.

Resources