Can't save UUID field in PHP script iteration, Laravel, Postgres - laravel

I'm trying to fill already existing models with UUID. And every time it says that UUID is not unique! What a bloody hell! )))
function unique_guid($model){
$guid = com_create_guid();
$table = $model->table;
$db_table = Db::table($table);
$req = $db_table->where("guid", strval($guid));
$instance = $req->first();
if(is_object($instance)){
$guid = unique_guid($model);
return;
}else{
try{
$model->guid = $guid;
$model->save();
sleep(2);
return $guid;
}catch(PDOException $Exception){
$guid = unique_guid($model);
}
}
}
It keeps circling in try/catch block and telling me that it is not unique,
i checked and there is no record with generated UUID.
Also - it brokes at third-fourth iteration, and if i add sleep(5) it works longer - 10 iteration and then brokes.
What in the world can it be?
Laravel 5.5, Postgres 9

Following the Laravel 6.0 official doc, I proceeded the following way:
I added the uuid-ossp extension at database creation. Then, my migrations were like:
use Illuminate\Database\Query\Expression;
...
public function up()
{
Schema::create('my_table', function (Blueprint $table) {
$table->uuid('guid')->primary()->default(new Expression('uuid_generate_v4()'));
...
});
}
I find it a lot more elegant like this.

I believe com_create_guid is not generating unique on every iteration, so please use some increment/prefix value to generated UUID, also if possible use uniqid instead of com_create_guid() uniqid generates id based on the current time in microseconds.
function unique_guid($model,$increment=0){
$guid = uniqid($increment,TRUE);
$table = $model->table;
$db_table = Db::table($table);
$req = $db_table->where("guid", strval($guid));
$instance = $req->first();
if(is_object($instance)){
$guid = unique_guid($model,++$increment);
return;
}else{
try{
$model->guid = $guid;
$model->save();
sleep(2);
return $guid;
}catch(PDOException $Exception){
$guid = unique_guid($model,++$increment);
}
}
}

I think you created function to generate UUID but there is some other better ways to generate UUID. You are using Laravel so you can search packages which are generating UUID.
There is a package I'm suggesting to use.
https://github.com/ramsey/uuid
These types of packages are useful and fully tested by community. So we can trust on them.

You can try like this.. it will check until the guid is really unique
public static function generateUniqueNumber($model = null, $column = null)
{
$num = null;
if ($model) {
$range = $model::all()->pluck('guid')->toArray();
while (in_array($num = com_create_guid(), $range)) {}
}
return $num;
}

I think it's some bug in Postgres 9.3. It inserting two rows at a time on some point of the script. I've eneded with just a migration like that
public function up()
{
DB::statement('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";');
DB::statement('ALTER TABLE feegleweb_octoshop_products ADD COLUMN guid uuid DEFAULT uuid_generate_v4() UNIQUE;');
}
It fill all my rows with unique uuid

Related

How to save data on different database tables in Laravel Backpack

how to save the data from the Features fields on a different table. Example in the link below, it is saving the Features table fields in a JSON field in the database. However, I want to save this data from the features into another table.
https://demo.backpackforelavel.com/admin/product/211/Edit
I'm coming back here to post my answer. I managed to settle, I'm putting here to help other developers.
This first part of the question I have already solved. But now I can not bring the data from the Features fields in the form.
Below is the source code that I was able to save and edit the form data. However, I can not carry the data from the Feature fields. Someone knows how I can carry the field data in Feature
class ProductCrudController extends CrudController
{
use ListOperation;
use CreateOperation {
store as traitStore;
}
use UpdateOperation {
update as traitUpdate;
}
use DeleteOperation;
public function store()
{
// insert item in the db
$item = $this->crud->create($this->crud->getRequest()->except(['save_action', '_token', '_method']));
$features = json_decode($this->crud->getRequest()->input('features'));
foreach ($features as $itens) {
$obj = new Feature();
$obj->product_id = $item->getKey();
$obj->name = $itens->name;
$obj->save();
}
// show a success message
\Alert::success(trans('backpack::crud.insert_success'))->flash();
return redirect('admin/product');
}
public function update()
{
$featureForm = json_decode($this->crud->getRequest()->input('features'));
// insert item in the db
$item = $this->crud->update($this->crud->getRequest()->id, $this->crud->getRequest()->except(['save_action', '_token', '_method', 'features']));
$objF = Feature::where('product_id', $item->getKey())->get();
foreach ($objF as $itens) {
Feature::where('id', $itens->id)->delete();
}
foreach ($featureForm as $itens) {
$obj = new Feature;
$obj->product_id = $item->getKey();
$obj->name = $itens->name;
$obj->save();
}
// show a success message
\Alert::success(trans('backpack::crud.insert_success'))->flash();
return redirect('admin/product');
}
}
I'm coming back here to post my answer. I managed to settle, I'm putting here to help other developers.
I was able to bring the data in the form edition, in the Features fields. I used the Mutators in the Model - https://laravel.com/docs/8.x/eloquent-mutators
Below is the example I did.
It would be interesting to have an example of this in the official documentation or demo project. I believe it would help a lot also to other developers.
//model Product
public function getFeaturesAttribute($value)
{
$objects = Features::where('product_id', $this->id)->get();
$array = [];
foreach ($objects as $itens) {
$obj = new stdClass;
$obj->name = $itens->name;
$array[] = $obj;
}
return \json_encode($array);
}
There are a few ways to go about it. Personally I prefer to create accessors and mutators in the model - https://laravel.com/docs/8.x/eloquent-mutators . Not only does it keep the CrudController clean (so updateable) but it also allows you to save/load stuff from other forms (ex: user-facing forms).
//model Product
public function getFeaturesAttribute($value)
{
$objects = Features::where('product_id', $this->id)->get();
$array = [];
foreach ($objects as $itens) {
$obj = new stdClass;
$obj->name = $itens->name;
$array[] = $obj;
}
return json_encode($array);
}

Updating Multiple columns of database with multiple text fields in Laravel

I want to update three fields in a three column of my database. Each time i run this, the last field get updated into all fields. Someone please help. Thanks in advance.
public function updateResult(Request $request)
{
$id = $request->student_id;
if(count($request->mark) > 0)
{
foreach($request->mark as $item=>$v){
$data=array(
'mark'=>$request->mark[$item],
'grade'=>$request->grade[$item],
'student_id'=>$request->student_id[$item],
);
Result::where('student_id', $id)->update($data);
}
}
return Redirect()->back();
}
You have fixed id here $id = $request->student_id and everytime you update Result you are updating same row(s) again.
I think you should do this:
public function updateResult(Request $request)
{
// $id = $request->student_id;
// Note: The line above is no longer needed
if(count($request->mark) > 0){
foreach($request->mark as $item=>$v){
$data=array(
'mark'=>$request->mark[$item],
'grade'=>$request->grade[$item]
// Note: There is no need to include student_id here unless you actually intend to change the ID
);
Result::where('student_id', $request->student_id[$item])->update($data);
// Note: In the line above, I replaced $id with $request->student_id[$item] to update the specific student
}
}
return Redirect()->back();
}
Hope this helps you

how to create custom auto increment for string field in laravel

i need to save a custom auto increment string when a new product is created. What is the best way to achieve this in laravel
this is the outcome that i want
id sku product_name
1 'sku-001' milk
2 'sku-002' cerelac
2 'sku-003' milo
this is my current code
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('sku');
$table->string('name')->unique();
});
You could also use model event's. Below you have the boot function in which you would put into your model. After that you would have access to various of event's like creating, created, updating, updated .. so on and so for. We are interested in created, as it is triggered after your model is saved.
The static method receives a function that has as parameter an instance of the model that was just saved so you can access the id and concatenate with the string you want.
/**
* Enables us to hook into model event's
*
* #return void
*/
public static function boot()
{
parent::boot();
static::created(function($product) {
$product->sku .= 'sku-' . $product->id;
$product->save();
});
}
Yes sure, but you have to generate it in your own. like this one
$id = "sku-000";
echo ++$id; // sku-001
echo "<br>";
echo ++$id; // sku-002
Remember that you to have fetch first the last inserted Id and get the sku of this then do the increment process. like this one
$last_id = Product::orderBy('sku', 'desc')->first()->sku;
$last_id = $last_id++;
Product::create(['sku' => $last_id, 'name' => 'test product']);
Try This Method:
$latestorder = Order::where('status','processing')->latest('created_at')->first();
if($latestorder->exists()) {
$total = $latestorder->total_count+1;
} else {
$total = 1;
}
$order->status = 'processing';
$order->total_count = $total;

What's the solution for adding unique slug to prevent duplicate entry in creating?

i'm working on a project for my homework which i'm trying to make it work as a spa, but I've got some problem in making slug for posts.
in the tutorial which i've followed, instructor used this to make a slug from title :
protected static function boot()
{
parent::boot();
static::creating(function ($course){
$course->slug = str_slug($course->name);
});
}
now, if i make this table unique, which this is what i wanna do. how should i prevent app from giving me duplicate entry? or how can i add something to slug, like a number, Every time i get duplicate entry?
if i make a post with This Post name twice, second time, i get duplicate Error.
In my opinion your selected answer is not getting close to something efficient. In large applications the 2 random strings can be overwritten in a short time and then you will have huge issues (code and DB).
A safer approach is to build a service and use that when you save the slug in the DB. Or course this is not 100% perfect but definitely is better then to increment 2 random strings. That, by the way, can also affect the SEO part of the app.
Below you can find my example:
The Model
public static function boot()
{
parent::boot();
static::saving(function ($model) {
$slug = new Slug();
$model->slug = $slug->createSlug($model->title);
});
}
The Service
<?php
namespace App\Services;
use App\Job;
class Slug
{
/**
* #param $title
* #param int $id
* #return string
* #throws \Exception
*/
public function createSlug($title, $id = 0)
{
// Normalize the title
$slug = str_slug($title);
// Get any that could possibly be related.
// This cuts the queries down by doing it once.
$allSlugs = $this->getRelatedSlugs($slug, $id);
// If we haven't used it before then we are all good.
if (!$allSlugs->contains('slug', $slug)) {
return $slug;
}
// Just append numbers like a savage until we find not used.
for ($i = 1; $i <= 100; $i++) {
$newSlug = $slug . '-' . $i;
if (!$allSlugs->contains('slug', $newSlug)) {
return $newSlug;
}
}
throw new \Exception('Can not create a unique slug');
}
protected function getRelatedSlugs($slug, $id = 0)
{
return Model::select('slug')->where('slug', 'like', $slug . '%')
->where('id', '<>', $id)
->get();
}
}
You could use inbuilt Str class, and create some random strings in your Post slug. Example:
static::creating(function ($course){
$course->slug = str_slug($course->name . Str::random( 2 ));
});
This will add 2 random strings on each slug you create, which will ensure there are no duplicates. You can find more about Str class here.
you can use laravel Inbuilt Helper, follow below link.
https://laravel.com/docs/5.8/helpers#method-str-slug
$data = 'My Data'
$slug = Str::slug($data, '-');
dd($slug);
add this in your controller
use Illuminate\Support\Str;

How to update record with OneToMany relationship in Doctrine 2?

I have a user entity and I'm trying to update it from a UserService. The problem comes when I try to update a property which is set as an array collection.
/**
*
* #param \Doctring\Common\Collections\Collection $property
* #OneToMany(targetEntity="Countries",mappedBy="user", cascade={"persist", "remove"})
*/
private $countries;
I'm not sure if I'm supposed to somehow delete all the $countries before I set them back or if there's a way to select which ones to delete and set up the different ones....this is what my updateUser method looks so far:
public function updateUser($user) {
$entity = $this->getUser($user['id']);
if (!$entity)
throw new Exception('Error saving user!');
$countries = $this->getCountriesArray($user); //countries already set in the db
$tempCountries = array();
$delete = array();
foreach ($countries as $country) {
$found = false;
if (in_array($country, $user['countries'])) {
$tempCountries[] = $country;
} else {
$delete[] = $country;
}
}
$tempCountries = array_unique(array_merge( //combine the countries from the db we want to leave
$tempCountries, //with those the user selected
$user['countries']));
...
//here I need something to remove the countries in $delete...right?
...
$entity->setEmail($user['email']);
$entity->setResponsable($user['responsable']);
$entity->setPassword($this->createPass($user['password']));
$entity->setUrl($user['url']);
$entity->setRole($user['role']);
$modelCountries = array();
foreach($tempCountries as $c) {
$p = new Countries();
$p->setCountryName($c);
$p->setUser($entity);
$modelCountries[] = $p;
}
$entity->setCountries($modelCountries);
$this->em->persist($entity);
$this->em->flush();
}
please stackOverflow... give me a hand making sense out of this.
It actually depends on your use case.
As you said, you can either delete all countries before to set the new ones, or compute the delta, and update only the needed ones.
What countries are you providing to your service? The delta? Or full list?
Do you have performance constraints? If yes, what is the cost of a DELETE statements vs SELECT then UPDATE?
Compute delta, then UPDATE can be tricky and difficult, in most case, you better want to just DELETE all and INSERT.
For my current and personal choice I am using DQL to DELETE all owing side rows for the mapped entity and then inserting the new one.
class Rule
{
/**
* #OneToMany(targetEntity="Tier", mappedBy="rule", cascade={"persist", "remove"})
* #JoinColumn(name="ruleId", referencedColumnName="ruleId")
* #var Tier[]
*/
public $tiers;
So when I am passing new Tier in my call I am simply Deleting the all Tiers for that Role from the Rule Mapper
public function resetTiers($ruleId)
{
$modelClass = "Tier";
$q = $this->doctrineEntityManager->createQuery("Delete from $modelClass m where m.rule =" . $ruleId);
$numDeleted = $q->execute();
return $numDeleted;
}
and then calling the usual
$this->doctrineEntityManager->persist($rule);
$this->doctrineEntityManager->flush();
Hope that helps

Resources