Best way to bulk create models within Laravel? - laravel

I am creating a feature which involves creating voucher codes for discounts. I contain an input which the user can fill out with the specific amount they want.
When it hits the store method say if the user has entered 15 vouchers how would you create that many model records in one go?
From looking at the laravel documentation the only thing i've seen is using factories so i've attempted this below.
public function storeCodes(Request $request, VoucherGroup $voucherGroup)
{
VoucherCode::create([
'code' => $this->generateUniqueCode(),
'group_id' => $voucherGroup->id,
'used' => 0
]);
session()->flash('success', 'successfully created codes.');
return back();
}
private function generateUniqueCode()
{
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersNumber = strlen($characters);
$code = '';
while (strlen($code) < 6) {
$position = rand(0, $charactersNumber - 1);
$character = $characters[$position];
$code = $code . $character;
}
if (VoucherCode::where('code', $code)->exists()) {
dd('lol');
$this->generateUniqueCode();
}
return $code;
}
Input
<x-inputs.input size="6" type="number" name="voucher_amount" required>How many Voucher codes do you want
to
generate?
</x-inputs.input>
The problem is it creates the models but the code returns the same code for each model.
How would I create 15 models in one call with unique code fields?

The for loop below solved this for me. It was originally posted by someone but its been deleted recently so ill answer the question below but thanks to whoever provided it!
$newVouchers = [];
for ($i = 0; $i < $request->voucher_amount; $i++) {
$newVouchers[] = VoucherCode::create([
'code' => $this->generateUniqueCode(),
'group_id' => $voucherGroup->id,
'used' => 0
]);
}

You can try it shortly
do {
//generate unique code
$uniqueCode = strtoupper(substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, 15));
} while (VoucherCode::where('code', $uniqueCode)->exists());
VoucherCode::create([
'code' => $uniqueCode,
'group_id' => $voucherGroup->id,
'used' => 0
]);
session()->flash('success', 'successfully created codes.');
return back();

You can use Insert Statements to bulk create models
$vouchers = [];
for ($i = 0; $i < $numberOfVoucher; $i++) {
$vouchers[] = [
'code' => $this->generateUniqueCode(),
'group_id' => $voucherGroup->id,
'used' => 0
];
}
DB::table('users')->insert($vouchers);

Related

Saving multiple model using array

Basically, I have an array of $course_id. I want to save multiple times according to number of $course_id
Here is my code:
$courselist[] = new Courselist;
for($i=0;$i<count($course_id);$i++)
{
$comparelist = Courselist::select('course_id','internal','external')
->where('course_id','=',$course_id[$i])
->get();
if($comparelist->isEmpty())
{
$courselist[$i]->course_id = $course_id[$i];
$courselist[$i]->internal = $internal;
$courselist[$i]->external = $external;
$courselist[$i]->save();
}
}
Does anyone know how can I save this?
you can use updateOrCreate() function like this
for ($i = 0; $i < count($course_id); $i++) {
Courselist::updateOrCreate([
['course_id' => $course_id[$i]],
[
'internal' => $internal,
'external' => $external
],
]);
}
ref link https://laravel.com/docs/8.x/eloquent#upserts
I use insert() function and it works
for($i=0;$i<count($course_id);$i++)
{
$data[] = array(
'course_id' => $course_id[$i],
'internal' => $internal,
'external' => $external
);
}
Courselist::insert($data);

Laravel avoid duplication between existing data and new inputs

I have an edit form where it shows users, sub-users and I need to make my update method the way that it can update existed sub-users as well as add new ones.
Logic
User can have a maximum of 5 sub-users
Let's assume my user already have 2 sub-users s/he can add up to 3 more
I want to be able to update those 2 that are existed already
I want to be able to add those 3 new sub-users
Code
Here is what I have currently. Code is commented for better understanding
public function subUsersUpdate(Request $request, $id) {
$will = Will::where('id', $id)->where('user_id', Auth::id())->first();
// main user
$user = User::where('id', $will->user_id)->first();
// making custom emails for new users like (admin1#example.com)
// admin is email name of main user, originally (admin#gmail.com)
// 1~5 are added to names (separate those 5 sub-users)
// example.com is website domain
$userMail = substr($user->email, 0, strpos($user->email, '#'));
$websiteName = env('DOMAIN_NAME');
// here is what I get from edit form including 2 existed sub-users and 3 new sub-users
foreach($request->input('subs') as $index => $sub) {
// trying to separate existed sub-users from new ones
$existedUser = User::where('name', $sub)->where('user_id', $user->id)->first();
if($existedUser) {
// if already existed just update the name
$existedUser->update(['name' => $sub]);
} else {
// if new add them as new sub-user
$num = $index+1;
$password = str_random(15);
User::create([
'name' => $sub,
'email' => $userMail . $num ."#" . $websiteName,
'password' => Hash::make($password),
'user_id' => $user->id,
]);
}
}
return redirect()->back();
}
Error
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'admin4#example.com' for key 'users_email_unique'
It appears that my code works in order to separate old and new sub-users but the problem is it makes the same email for new-users as old ones based on $num provided in my foreach. I need somehow avoid that duplication.
Any suggestion?
Update
What I did
I've added hidden input in my form which has sub-user email as value (if user existed) and if user is newly added it is null.
Then I merged those emails array and names array into one
Finally I've validate if the email field is null or not in order to update or create new sub-user.
Issue
Issue is name of all sub-users will be same as last input (sub-user 5), regardless of user being newly created or updated all 5 get same name!
public function subUsersUpdate(Request $request, $id) {
$will = Will::where('id', $id)->where('user_id', Auth::id())->first();
$user = User::where('id', $will->user_id)->first();
$userMail = substr($user->email, 0, strpos($user->email, '#'));
$websiteName = env('DOMAIN_NAME');
//Changed part
$mails = $request->input('mails');
$names = $request->input('subs');
// created array of data
$finalInputs = [];
foreach($mails as $index => $mmm) {
foreach($names as $index2 => $nnn) {
$finalInputs[$index] = ['email' => $mmm, 'name' => $nnn];
}
}
foreach($finalInputs as $index => $sub) {
if(!empty($sub['email'])) {
$existedUser = User::where('email', $sub['email'])->where('user_id', $user->id)->first();
$existedUser->update(['name' => $sub['name']]);
// $userMail2 = substr($sub['email'], 0, strpos($sub['email'], '#'));
} else {
if($sub['name'] != null) {
$num = $index+1;
$password = str_random(15);
User::create([
'name' => $sub['name'],
'email' => $userMail . $num ."#" . $websiteName,
'password' => Hash::make($password),
'user_id' => $user->id,
]);
}
}
}
// end of changed parts
}
Using updateOrCreate()
updateOrCreate() can handle conditional updating or creating of records for you.
This could be what you are asking, hope you get the idea:
foreach($finalInputs as $index => $sub) {
$pwHash = Hash::make( str_random(15) );
$num = $index+1;
$search = [
'user_id' => $user->id,
'email' => $userMail . $num ."#" . $websiteName,
];
$update = [
'name' => $sub['name'],
];
// Only add 'password' for new users
if (empty($sub['email']))) {
$update['password'] = $pwHash;
}
User::updateOrCreate(
$search,
$update
);
}
This will update or create users with the given email address and user-id and create if they don't exist. I don't know how your form works in detail, but I think you can adjust to your exact use-case.

Laravel isDirty method mass assignment

My code is saving data of only one field(efirst) if it's changed by the isDirty() method, and it's working correctly. How can I achieve the same result if I have ten fields without writing each field name?
Controller:
public function update(TeacherRequest $request, $id)
{
$teacher = Teacher::find($id);
$teacher->efirst = $request->efirst;
if ($teacher->isDirty()) {
$new_data = $teacher->efirst;
$old_data = $teacher->getOriginal('efirst');
if ($teacher->save()) {
$teacher->update($request->except('qual_id', 'id', 'profile_pic'));
DB::table('teacher_logs')->insert(
[
'user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $old_data,
'new_value' => $new_data,
]);
}
}
}
If you don't want to write $teacher->field = $request->value; a bunch of times, you may use a loop:
foreach($request->except("_token") AS $field => $value){
$teacher->{$field} = $value;
}
if($teacher->isDirty()){
$new_data = [];
$old_data = [];
foreach($request->except("_token") AS $field => $value){
$new_data[$field] = $value;
$old_data[$field] = $teacher->getOriginal($field);
}
}
Note: You'll need to convert $new_data and $old_data to arrays so you can reference each field and value properly, and do some additional logic on the insert of your teacher_logs table to handle, but that should give you an idea.

Inserting and updating records from 3 tables in laravel

I am storing records for my product transfer app using 3 tables in single action. Transferhistories, Warehouse1StockSummaries and Warehouse2StockSummaries.
storing records to trasnferinghistories is ok, and also the increment method I declare to Warehouse2StockSummaries is also working fine except for Warehouse1StockSummaries.
here's my store function,
public function store(Request $request)
{
$input = $request->all();
$items = [];
for($i=0; $i<= count($input['product_id']); $i++) {
// if(empty($input['stock_in_qty'][$i]) || !is_numeric($input['stock_in_qty'][$i])) continue;
if(!isset($input['qty_in'][$i]) || !is_numeric($input['qty_in'][$i])) continue;
$acceptItem = [
'product_id' => $input['product_id'][$i],
'transfer_qty' => $input['qty_out'][$i],
'user_id' => $input['user_id'][$i]
];
array_push($items, Transferhistories::create($acceptItem));
// dd($input);
//update warehouse 1 summary
$warehouse1summary = Warehouse1StockSummaries::firstOrCreate(
['product_id' => $input['product_id'][$i]],
['qty_in' => $input['qty_in'][$i],
'qty_out' => $input['qty_out'][$i]
]);
if (!$warehouse1summary->wasRecentlyCreated) {
$warehouse1summary->increment('qty_out', $input['qty_out'][$i]);
}
//update warehouse 2 summary
$stock2Summary = Warehouse2StockSummaries::firstOrCreate(
['product_id' => $input['product_id'][$i]],
['qty_in' => $input['qty_out'][$i],'qty_out' => null]);
if (!$stock2Summary->wasRecentlyCreated) {
$stock2Summary->increment('qty_in', $input['qty_in'][$i]);
}
}
return redirect()->route('transferHistory.index');
}
updating warehouse 1 summary is not doing what it should be.
any suggestion master? thank you so much in advance!
According to laravel, firstOrCreate does not save the value, so after you do:
$warehouse1summary = Warehouse1StockSummaries::updateOrCreate(
['product_id' => $input['product_id'][$i]],
['qty_in' => $input['qty_in'][$i],
'qty_out' => $input['qty_out'][$i]
]);
Edit
The method firstOrNew will return the first or a instance of the Model.
So what you wanna do is this:
$warehouse1summary = Warehouse1StockSummaries::firstOrNew(
['product_id' => $input['product_id'][$i]],
['qty_in' => $input['qty_in'][$i],
'qty_out' => $input['qty_out'][$i]
]);
if(isset($warehouse1summary->created_at)){
$warehouse1summary->qty_out = $warehouse1summary->qty_out + $input['qty_out'][$i];
}
$warehouse1summary->save();

saving number field with a value of zero in Laravel 5.5

I have a form that accepts delivery of products which I noticed if I enter 0 in the quantity field it doesn't save in the database even if I add data in the calendar or in Notes field.
I already commented out the \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,iin kernel.php still doesn't work.
how can I forced laravel to save my data even if I want to put 0 in quantity? thanks in advance!
update
public function store(Request $request)
{
$input = $request->all();
$items = [];
for ($i = 0; $i <= count($input['order_id']); $i++) {
if (empty($input['stock_in_qty'][$i]) || !is_numeric($input['stock_in_qty'][$i])) continue;
$acceptItem = [
'order_id' => $input['order_id'][$i],
'product_id' => $input['product_id'][$i],
'order_item_id' => $input['order_item_id'][$i],
'delivery_date' => $input['delivery_date'][$i],
'company_id' => $input['company_id'][$i],
// 'stock_in_qty' => intval($input['stock_in_qty'])[$i],
'stock_in_qty' => $input['stock_in_qty'][$i],
// 'stock_out_qty' => $input['stock_out_qty'][$i],
// 'transfer_to' => $input['transfer_to'][$i],
'delivery_note' => $input['delivery_note'][$i],
'user_id' => $input['user_id'][$i],
];
array_push($items, Warehouse1stocks::create($acceptItem));
$stockSummary = Warehouse1StockSummaries::firstOrCreate(
['product_id' => $input['product_id'][$i]],
['qty_in' => $input['stock_in_qty'][$i],
'qty_out' => null,
]);
if (!$stockSummary->wasRecentlyCreated) {
$stockSummary->increment('qty_in', $input['stock_in_qty'][$i]);
}
}
if ($input['rd'] == $input['stock_in_qty'] || $input['rd'] == 0) {
$order_idAcceptedItem = $acceptItem['order_id'];
$setStatus = \App\Orders::where('id', '=', $order_idAcceptedItem)->first();
if ($setStatus) {
$setStatus->status_id = 4;
}
$setStatus->save();
} else {
$order_idAcceptedItem = $acceptItem['order_id'];
$setStatus = \App\Orders::where('id', '=', $order_idAcceptedItem)->first();
if ($setStatus) {
$setStatus->status_id = 3;
}
$setStatus->save();
}
return redirect()->route('orders.index');
}
empty() will return true with 0 or '0' which will mean that if you try to change the quantity to 0 the for loop will just continue on to the next loop. If you need to check if the value exists you can instead use isset().
Changing your first if statement to the following should be all you need:
if(!isset($input['stock_in_qty'][$i]) || !is_numeric($input['stock_in_qty'][$i])) continue;

Resources