how to update json key value in laravel - laravel

we try to update the json data key value.we stored record in database product_name column like
{"ar":"arabic product","en":""}
we try to achieve this both below two method
DB::table('products')
->where('id', 1)
->update(['productname->en' => 'english product']);
$post = product::find($product_id);
$post->product_name = [$language => $edittitle];
$post->save();
we want to update the json en key value.

A quick and easy solution to update all the entries in one go:
Product::each(function ($product) {
$productName = json_decode($product->product_name, true);
$productName['en'] = 'english product';
$product->product_name = json_encode($productName);
$product->save();
});
If you want to update a single product, this should work:
$product = Product::find($product_id);
$productName = json_decode($product->product_name, true);
$productName['en'] = 'english product';
$product->product_name = json_encode($productName);
$product->save();

First thing is to create that field with the right type in the migration file, the keyword we need there is json:
...
$table->json('productname');
...
Then we need to cast that attribute to the right type when retrieving it as Eloquent model by using $casts attribute on the model itself:
...
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'productname' => 'array'
];
...
Then we can access and save data for this attribute like so:
$myModel = product::find(1);
$myModel->productname['en'] = 'something';
// or
$myModel->productname = ['en' => 'something']; // This will override the whole attribute
For further details you can take a look at: https://laravel.com/docs/5.7/eloquent-mutators#attribute-casting

Related

Laravel 8.6 firstOrCreate on variable model from URL

In a URL i pass the table name with the where parameters en map this using a JSON. The problem is now is. I use DB::table($table) but this doesn't allow firstOrCreate so i wanted to try it by model, but $table::were doesn't see $table as a model name. Even if i change it from 'establisments' to 'Estalishment'. Maybe i need to put App\ before it? Because the model is not in use. How can i get the object using a model from model names as in the URL?
URL:
&spec_code=CHI&department=surgery&department_code=SUR
JSON:
{
"establishments": { // $table name, but want it to be model)
"from1": "spec_code", // from url
"to1": "abc_id" // to column in database
},
"departments": {
"from1": "department",
"to1": "name",
"from2": "department_code",
"to2": "abc_id"
},
}
Controller:
foreach(JSON as $table => $field){
if(isset($field['from2'])){
$output[$table] = DB::table($table)->where($field['to1'], $request->input($field['from1']))->where($field['to2'], $request->input($field['from2']))->first();
}elseif(isset($field['from1'])){
$output[$table] = DB::table($table)->where($field['to1'], $request->input($field['from1']))->first();
// $output[$table] = $table::where($field['to1'], $request->input($field['from1']))->firstOrCreate(); //i want this.
// $output[$table] = "\App\\".$table::where($field['to1'], $request->input($field['from1']))->firstOrCreate();
}else{
$output[$table] = null;
}
}
If someone knows how i can get it to use the model so i can firstOrCreate(), that would make my code a lot cleaner.
Kind regards,
Jeff
There are many ways to do this.
Create an array using which you can get model
$models = [
'establishments' => \App\Models\Establishment::class,
'departments' => \App\Models\Department::class,
]
Then within loop you can get the model from the array using
foreach(JSON as $table => $field){
$models[$table]::firstOrCreate(/* */)
}
As i said there are many ways to fulfill your requirement. this is one way i would do.
By Default laravel does't have out of the box method to get the Model FQN based on the table name.
Because you can have model under different namespace.
But however you can do a workaround
$tableClassToFind = 'users';
$models = [
\App\Models\User::class,
//add your model(s) here
];
$modelClass = collect($models)
->map(function($eachModelFqn){
return [
'modelClass' => $eachModelFqn,
'tableName' => app($eachModelFqn)->getTable(),
];
})
->mapWithKeys(fn($value) => [$value['tableName'] => $value['modelClass']])
->get($tableClassToFind);
dd($modelClass);
Basically it will get all the table name along with the Model FQN and changing the value of $tableClassToFind will get the appropriate Model class.
Please all the models to $models variable.

store and update an array as json in mysql database laravel 9

I have values that I want to store in the database, I have declared the type as json and cast it as an array:
protected $casts = [
'favourites' => 'array'
];
however, I'm not sure how to add and update and read the values inside, any ideas?
controller function:
public function addtofav()
{
$id = request()->get('id');
$user = auth()->user();
json_decode($user->favourites,true);
array_push($user->favourites,$id);
dump('hello');
json_encode($user->favourites);
$user->save();
return response(['id'=> $id],200);
}
Quoting from the Laravel documenations:
adding the array cast to that attribute will automatically deserialize
the attribute to a PHP array when you access it on your Eloquent
model.
therefore, no need to make any encoding or decoding to your values before any update or create, just keep it as an array and Eloquent will take care of that, since you have the $cast array as below:
protected $casts = [
'options' => 'array',
];
Actually you should use many to many but if you want to use array, you don't encode or decode.
public function addtofav()
{
$id = request()->get('id');
$user = auth()->user();
$favourites = $user->favourites;
array_push($favourites,$id);
$user->favourites = $favourites
$user->save();
return response(['id'=> $id],200);
}

Laravel 6 validate multiple columns

Using the following code to create and update records with Laravel 6, how to add a unique validation that combines 'iso' and 'division' columns must unique, for both update and create? Also how to update the 'updated_at' column automatically without 'touch' it?
public function upsert(Request $request)
{
$this->authorize('manage','App\Admin\Division');
$this->validate($request,[
'records.iso' => 'required|regex:/^[A-Z]{2}$/',
'records.division' => 'required|min:2|max:3',
'records.remarks' => 'min:2|max:255|string|nullable',
],
[ 'records.iso.required'=>'Country iso Requied',
'records.iso.regex'=>'Country iso format : AA',
'records.division.required'=>'Division Code must entered',
'records.division.min'=>'Division Code minimum 2 characters',
'records.division.max'=>'Division Code maximum 3 characters',
],
);
$validatedData = $request->records;
if($validatedData['id']){ // have id, old record -> update
Division::where('id', $validatedData['id'])->update($validatedData);
//*******/ To correct updated_at no auto updated
$results = Division::find($validatedData['id']);
$results->touch();
//****** */ End of correction
}else{ // no id -> create record
$validatedData['auth'] = Auth::id();
$results = Division::create($validatedData);
}
return ['divisions' => $results];
}
All the validation rules apply for each field, i think you can do a foreach to workaround and compact the code, something like:
foreach ($records as $key => $record) {
$fields['record'.$key] = 'iso', 'division';
$rules['record'.$key] = 'required|min:2';
}
$validator = Validator::make($fields, $rules);
In the rules array you can put all the shared rules, but you have to create the other complex validation rules after this, so i think that your validation now is just fine.
And with the updated_at field, use the updateOrCreate
$results = Division::updateOrCreate(
['id', $validatedData['id'],
[$validatedData]
);
Adapt the above example for your code.

yii2 get getter's value in active query request (using asArray())

I have the next code:
$fixed_events = EventMain::find()
->select(["id", "title", "files"])
//->joinWith(['files'])
//->with(['files'])
->asArray()
->all();
How can i get array with "files" value, taking into account, that the "files" is modle's getter like
public function getFiles()
{
return (json_decode($this->all_files, true)) ?: [];
}
Since files is not a relation with EventMain table, I guess the easiest approach would be handle the data and convert with ArrayHelper after coming from db:
<?php
use yii\helpers\ArrayHelper;
$models = EventMain::find()->select(['id', 'title'])->all();
$array = ArrayHelper::toArray($models, [
'app\models\EventMain' => ['id', 'title','files']
]);
var_dump($array);
?>

Validate single rows only when they are present Laravel

So i have a model named Customer.
The db the Customer looks like this:
id, name, lastName, personal, address, zip, location, phones, emails updated_at, created_at
Emails and Phones is special rows because they are store as an json object example
['john#doe.com', 'some#othermail.com', 'more#mails.com']
I use the Customer Model to store the validation rules and custom messages like this
<?php
class Customer extends BaseModel
{
public function validationRules()
{
return array(
'name' => 'required|max:255',
'lastName' =>'max:255',
'personal'=> 'integer',
'location' => 'max:255',
'address' => 'max:255',
'zip' => 'required|integer',
'phones' => 'betweenOrArray:8,10|required_without:emails',
'emails' => 'emailOrArray'
);
}
public function validationMessages()
{
// returns Validation Messages (its too much to write down)
}
}
The OrArray Rule is found here https://stackoverflow.com/a/18163546/1430587
I call them through my controller like this
public function store()
{
$customer = new Customer;
$messages = $customer->validationMessages();
$rules = $customer->validationRules();
$input['name'] = Input::get('name');
$input['lastName'] = Input::get('lastName');
$input['personal'] = preg_replace("/[^0-9]/", "", Input::get('personal'));
$input['location'] = Input::get('location');
$input['address'] = Input::get('address');
$input['zip'] = Input::get('zip');
$input['emails'] = Input::get('emails');
$input['phones'] = Input::get('phones');
foreach($input['phones'] as $i => $value)
{
$input['phones'][$i] = preg_replace("/[^0-9]/", "", $value);
}
$validator = Validator::make($input, $rules, $messages);
}
This all works just fine, but I want to be able to PUT/PATCH request to update a single row.
But the validationRules has Required on certain fields so when its not present i cant update that single row. Without getting an error that the other fields (witch I'm not posting) is required.
How would I best approach this ?
You should get that instance of the model that represent the row you want to edit, that's why the resource controller's update method has a parameter that is the resource you want to edit.
public function update($resourceId) {
$customer = Customer::where('id', '=', $resourceId);
}
Now this customer has all the attributes you set before, so you can access them like:
$customer->name;
$customer->lastName;
So when you valide the values you can use the existing values in your validator where the input is empty:
$input['name'] = (Input::get('name')) ? (Input::get('name')) : $customer->name;
Or a prettier solution with the elvis operator:
$input['name'] = (Input::get('name')) ?: $customer->name;
I came up with another solution to this problem that works very well and its much cleaner.
$customer = Customer::find($id);
$input = Input::except('_method', '_token');
$customer->fill($input);

Resources