How to set sequence data with existing random data? - php

I have a table entity named it as uniqueId where the entry generate randomly.Such as
$customer->uniqueId = $request->Input(['uniqueId']) ?: mt_rand(1000, 9999);
means if there is existing uniqueId it will store the existing one otherwise it will be set to the random number. Now instead of setting the random number i want to set it as sequentially . means from 1, 2, 3 like that.. as i can't delete the existing uniqueId which has already created how do I create new entry sequentially with the existing one?

If you simply set column to auto increment you will achieve this automatically you dont even need to call it.
In laravel you can achieve this in your migrations by
$table->increments('uniqueId');
OR
You can achieve this by
lets assume you have a customer Model
// find the last entry in you table
$oldCustomer = Customer::orderBy('uniqueId','DESC')->first();
$customer->uniqueId = ++($olderCustomer->uniqueId);
I hope this helps
**EDIT **
$customers = Customer::all();
$index = 1;
#foreach($customers as $customer)
{
$customer->uniqueID = $index++;
$customer->update();
}

Related

Laravel Eloquent: any way to update DB with dynamic fields?

I am trying to implement the "Edit Application Settings" feature. After a bit of thinking, my configuration values are stored in the DB with key -> value structure, like this:
id
key
value
1
logo_path
img/logo.png
As you can see, for each setting, there is only a key & value column. I made an App Service provider to cache them forever, and a helper function (config('setting_key')) to get the value, but now I'd like to update it in the most efficient way.
The user interface consists of the <form action="post" ...> and input with a corresponding name, like this: <input name="setting_key_name" ... />. As you can see, the name attribute here has the value of the key column value and the actual value of the input would be the value column value (a bit of confusion here).
First thing that came to my mind, was to make a foreach loop and find & update every row in DB, but IMHO it is very unoptimized way, cause if the page has a form with 10 values, it is 10 SQL queries. But till now, this is what I've done:
$keys = collect($request->except('_token'))->keys()->toArray();
// get all settings if the key name matches the request's input name
$setting = Setting::whereIn('key', $keys)->get();
$logo = self::GENERAL_APP_LOGO; // contant with a key-name (general_application_logo);
if($request->has(self::GENERAL_APP_LOGO) && $request->$logo) {
// Processing uploaded image here;
$this->uploadLogo($image, self::LOGO_IMAGE_PATH, $name); // Using an upload trait
$setting->where('key', $logo)->value = self::LOGO_IMAGE_PATH . $name; // just a try to update the DB this way
}
foreach ($keys as $key) {
$setting->where('key', $key)->value = $request->$key; // putting all request's input values to corresponding key
}
$setting->save(); // saving the DB.
As you can see, this won't work and will throw an Exception, like Call to undefined method ...\Eloquent\Builder::save(). I tried the same code with an update, but the difficult part here is to update it multiple times (since the if section should have the update as well, for the logo), as well as binding the key to value.
So, a little bit of your help would be appreciated - what the logic should be here? How can I update a DB rows with corresponding column's value? I mean - like this (update where key = 'general_app_name' set value, 'some_setting_value'), but using the optimized and clear way?
Working solution
As #miken32 stated in his answer, I used hid version of code, but with slight changes:
// Changed the $request->settings->keys() to PHP native method array_keys():
$settings = Settings::whereIn('key', array_keys($request->settings))->get()->groupBy('id');
// Also, here I changed the `whereIn('id', ...)` to `whereIn('key', ...)`, since it was my primary index.
foreach ($request->settings as $k=>$v) {
if ($k === self::GENERAL_APP_LOGO_ID) {
// not sure about this one, but I think this is
// how you'd access a file input in an array
$image = $request->file('settings')[$k];
$this->uploadLogo($image, self::LOGO_IMAGE_PATH, $name);
$v = self::LOGO_IMAGE_PATH . $name;
}
// take the Setting object out of the list we pulled
// Here I added the ->first() to get the first element from the retrieved collection;
$setting = $settings->get($k)->first();
$setting->value = $v;
$setting->save();
}
Since I was fetching the configuration values via helper, that only returns the value of the current key (and no id column), I changed the id to key and made the key as my PK in a model. Works like a charm!
With each setting in a separate row, there's no way to avoid multiple database queries – one to get the current values for all settings, and other to update each one. Looking up items by primary key is more efficient, so I'd recommend putting the contents of the id column in your blade view, like this:
<label for="setting_{{$setting->id}}">{{$setting->key}}</label>
<input name="settings[{{$setting->id}}]" id="setting_{{$setting->id}}" value="{{$setting->value}}"/>
Now in your controller, $request->settings will be an array you can loop through. You can continue treating your file upload separately, but now you've got the id column to look up, so change your constant to that.
$settings = Settings::whereIn('id', $request->settings->keys())->get()->groupBy('id');
foreach ($request->settings as $k=>$v) {
if ($k === self::GENERAL_APP_LOGO_ID) {
// not sure about this one, but I think this is
// how you'd access a file input in an array
$image = $request->file('settings')[$k];
$this->uploadLogo($image, self::LOGO_IMAGE_PATH, $name);
$v = self::LOGO_IMAGE_PATH . $name;
}
// take the Setting object out of the list we pulled
$setting = $settings->get($k);
$setting->value = $v;
$setting->save();
}
Note that Laravel does offer methods to bulk-update multiple models at once, but they are doing separate queries to the database in the background. IIRC, the save() method doesn't do anything if the value hasn't changed, which will spare you some hits.
You could try creating a text field, or a json field if your database supports it, and storing all of your settings as a JSON string in that field.
id
settings
1
{ "logo_path" : "img/logo.png", "foo" : "bar", "thing_count" : 17 }
2
{ "logo_path" : "img/logo2.png", "foo" : "baz", "thing_count" : 4 }
In your Laravel model, you can cast it as an array
protected $casts = ["settings" => "array"];
and then use it from the model
echo $theModel->settings['logo'];
echo $theModel->settings['foo'];
or you can cast it as a fully fledged object if you need to using value object casting.
One gotcha that can be confusing for people is the setting of the values in the array to update it. This will not work:
$theModel->settings['foo'] = "boz";
The reason is due to the way the Laravel mutators work. Instead, you make a value copy of the settings, change that, and reassign it to the model:
$settings = $theModel->settings;
$settings['foo'] = "boz";
$theModel->settings = $settings;
This approach has the capacity to infinitely expandable in the future as you just add new keys to your json. Be sure to do checks on the settings array to ensure fields you are looking for are set (which is why value objects can be very handy to do validation).
It also solves your database query problem - it's only ever one.
You don't need to put
$setting->where('key', $logo)->value = ...;
Just call
$setting->where('key', $logo)->update($request->toArray());
$setting->save(); called when you instantiated setting class like :
$setting = new Setting();
Or
$setting = Setting::whereIn('key', $keys)->get()->first();
Then
$setting->val = ...;
$setting->save(); // then it work's

Make unique ID using Laravel

I want to generate a unique id in Laravel.
EX: PO-12010001
PO = product,
12 = the month,
01 = the year,
0001 = ID of product.
I have tried googling and the answer is using UUID but could not understand.
Your ID will always be 4 digits at the end, so we can pluck those last four characters using substr(). When you increment that by one, it will lose its padding. So 0001+1=2. We therefor pad it back using str_pad() with a length of four.
$string = 'PO-12010001';
$id = substr($string, -4, 4);
$newID = $id+1;
$newID = str_pad($newID, 4, '0', STR_PAD_LEFT);
echo "PO-1201".$newID;
Live demo at https://3v4l.org/55RTL
Since it is not clear where the last 4 characters (ID of product) come from, there are 2 different outcomes based on their origin, I am assuming that "PO" is a constant in all your products:
1.If you're auto-generating the Product ID:
$id = "PO-".date('my') . substr(uniqid(), 9, 12);
date('my') will return a two-digits form of the current month and a two-digits form of the current year.
uniqid() returns a unique identifier based on the current time in microseconds, the identifier is usually a mix of letters and digits, and it is usually 13 characters long, so we use substr() to only return the last 4 characters of the string.
NOTE: we are using the last characters of uniqid() because they change every millisecond.
2.If you already have a Product ID:
$id = "PO-".date('my') . $product_id;
When like this situation first how you want to make your ID
Item Type
Month
Year
Product ID
Get product type in your controller
if Product = PO /
Service = SE
Create a New Date using PHP
$date2 = date('Y-m-d');
and get date substring with your requirement and get the last id of the product table and concat all the variable and use as Unique ID.
First, I dopn't know. So maybe it has a similar concept. But anyway, user defined+designed unique are most likely not unique.
My recommendation is to let the database create an unique id with the autoincrement feature. This is usually the only way to guarantee unique ideas in a multi tasking environment.
The, in an second step, you can create an human readable id and use it for displaying it at the suer interface. Such query can be something like:
update table set nice_id = concat("prefix-",main_id)
where main_id = $last_inserted_id
... or any other calculation based on counting the the number of same entries since beginning of month.
There are other solutions based on try to create an nice_id, insert it into the database, and if this fails, create the next one .. and loop until successful. But simple integers created by autoincrement are more performant on queries and for keys.
it's called Human code which can be unique identify beside an Id column in db table.
$idColumn = 1;
$dateCode = date('ym');
$newHumanCode = 'PO-'.$dateCode.substr('0000'.$idColumn, -4);
return $newHumanCode;
also you can use randome number instead of use $idColumn,
for example:
$idColumn = mt_rand();
You can use the Laravel ID generator.
First Install it:
composer require haruncpi/laravel-id-generator
Import the class in your controller.
use Haruncpi\LaravelIdGenerator\IdGenerator;
Now simply use it
$prefix = "PO-".date("my");
$id = IdGenerator::generate(['table' => 'your_table_name', 'length' => 11, 'prefix' =>$prefix]);
Output
PO-12010001
PO-12010002
PO-12010003
...

Laravel - can't find() attached object in many-to-many relationship

in my POST form users are able to add other users to a room.
I put a unique constraint on the link (no duplicate entry in the link between users and rooms).
However when I refresh my page (f5) after submitting the form, Laravel complains about duplicate entries, although I do check if the objects are attached before.
Here's the code:
$roomUsers = Room::find($request->room_id)->users();
if ($request->add != null) {
foreach ($request->add as $uId)
// if null, user hasnt been attach yet
if (!$roomUsers->find($uId)) {
Log::debug($roomUsers->find($uId) == null ? 'null' : 'not null');
// then we can attach him
$roomUsers->attach($uId);
}
}
The line !$roomUsers->find($uId) returns true yet the object has been attached in the previous iteration. How is that possible ? Thanks
The reason you're above code isn't working is because you're not creating a new instance of BelongsToMany for each check. This means that every time you call find you're not actually creating a new query you're just adding to the existing one e.g.
say you the ids to add are [1, 2, 3] by the last check your query would effectively be:
SELECT * FROM users WHERE id = 1 AND id = 2 AND id = 3
To keep with the above logic you could do:
$room = Room::find($request->room_id);
if ($request->add != null) {
foreach ($request->add as $uId)
// if null, user hasnt been attach yet
if (!$room->users()->find($uId)) {
// then we can attach him
$room->users()->attach($uId);
}
}
Or a much simpler way to go about this would be to syncWithoutDetaching.
Your code could then look something like:
$roomUsers = Room::find($request->room_id);
if ($request->has('add')) {
$roomUsers->users()->syncWithoutDetaching($request->add);
}
Hope this helps!

Laravel - How to pass different values for pivot data table in sync() function

My form sends a $request->assets that I am able to sync using the following:
$user->assets()->sync($request->assets === null ? [] : $syncData);
The complexity (not a problem) arises when I try to retrieve values from a serial number array that is sent back as $request->serialnumber
I have set up the serial numbers in such a way that the serial number array index corresponds to my id column in the assets table. E.g. if Mobile has a value of 1 in the database, it is located in $request->serialnumber[1], and for something that would have an id of 2, would be placed in $request->serialnumber[2] and so on.
I have done the following so far, in order to insert the correct serial numbers for the correct asset() relationship:
$serialData = [];
$pivotData = [];
$syncData = [];
//for every assigned asset, build an array of their serial numbers from the serial number array...
if(isset($request->assets))
{
for($i = 0; $i<count($request->assets);$i++)
$serialData[$i] = $request->serialnumber[$i];
}
//if some serials were set, use them for pivot data
if(count($serialData)>0)
{
$filledArray = array_fill_keys($serialData,"serialnumber");
$pivotData = array_flip($filledArray);
}
$syncData = array_combine($request->assets, $pivotData);
I know its a long drawn out way, so I'm wondering if there's an easier way to do this?

Symfony: Delete all the records in a table who have the id in the string in table's column

[Please note that I do not have code for this problem, I need code, I have tried to explain it the best way, and if you can help, it will be great]
so here is the deal, I have a field in the table named "order" for every user. The main job of the user is to bring other users to the system and when they bring a new user their id is sticked to (concated with) the referring user's id and stored in his "order field"
for eg.
user 'a' has id 31. 'a' brings in 'b' whose is assigned 32, now b's therefore b has the value: '31-32' stored in his 'order' field. simililarly if b brings in 'c' whose id is 35, the order for c will be: '31-32-35' and it goes so on.
Now when I delete 'a' I want ALL the users who have his id in their order fields, that is if i decide to delete 'a' in this above field, all the users should be deleted from he system too!
I want to do this via symfony controller, and I think that can be done by findAll() function in symfony but I have no clue how to use it.
Please help, I am really stuck!
I did by using a parent field in each table for the user:
and here is the code if such thing arises for someone:
<?php
$repo = $em->getRepository('SystemBundle:Distributor');
$temp = $slug;
$pd = $repo->findAll();
for ($i = 1; $i <= count($pd); $i++) {
$u = $repo->findOneBy(['parent' => $temp]);
if ($u) {
$temp = $u->getId();
$em->remove($u);
$em->flush();
} else {
break;
}
}
$dis = $repo->findOneBy(["id" => $slug]);
$em->remove($dis);
$em->flush();
Using for loop it was possible to do this

Categories