Laravel update json column in postgresql - json

In postgresql db I have a column type json call it "activities". I would like to add there new (key -> value) pair. Without delete existing data.
At this moment my code look like this:
$user = User::with('userLife')->where(['id' =>$id])->first();
$user->userLife->confirmation_token = null;
$user->userLife->activities = ['emailConfirmed' => Carbon::now()->timestamp];
$user->email_confirmed = true;
$user->push();
In my Model I added:
protected $casts = [
'activities' => 'array',
];
But when I use this code all other data in column is removed and only 'emailConfirmed' appeared there.
So how to do it properly to just add new value in column without removing previous?
Thank you.

You should add it as new element through another variable:
$activities = $user->userLife->activities;
$activities['emailConfirmed'] = Carbon::now()->timestamp;
$user->userLife->activities = $activities;

Related

Keep sort order of json columns in Laravel after inserting new Key Value pair

I have a key value pair that I am inserting into a model with the following:
public function addContactDetail(Request $request){
$data = $request->all();
$contact_id = $data['contact_id'];
$contact = Contact::find($contact_id);
$details = $contact->details;
$details[$data['label']] = $data['value'];
$contact->details = $details;
$contact->save();
return response()->json($contact);
}
After insert it sometimes puts it randomly in the middle of the object. How do I keep it at the end?
If you are using Laravel 5 or greater version,
Try casting your json column into array in eloquent using mutators. like this.
inside your Contact Model
protected $casts = [
'details' => 'array',
];
By doing so, I guess you will get what you want. Try it and let me know

Retrieve specific data using JSON decode Laravel

I'm new to Laravel. I need to retrieve specific data from the database using the JSON decode. I am currently using $casts to my model to handle the JSON encode and decode.
This is my insert query with json encode:
$request->validate([
'subject' => 'required|max:255',
'concern' => 'required'
]);
$issue = new Issue;
$issue->subject = $request->subject;
$issue->url = $request->url;
$issue->details = $request->concern;
$issue->created_by = $request->userid;
$issue->user_data = $request->user_data; //field that use json encode
$issue->status = 2; // 1 means draft
$issue->email = $request->email;
$issue->data = '';
$issue->save();
The user_data contains {"id":37,"first_name":"Brian","middle_name":"","last_name":"Belen","email":"arcega52#gmail.com","username":"BLB-Student1","avatar":"avatars\/20170623133042-49.png"}
This is my output:
{{$issue->user_data}}
What I need to retrieve is only the first_name, middle_name, and last_name. How am I supposed to achieve that? Thank you in ADVANCE!!!!!
As per the above code shown by you it will only insert data into the database.For retrieving data you can make use of Query Builder as i have written below and also you can check the docs
$users = DB::table('name of table')->select('first_name', 'middle_name', 'last_name')->get();
I will recommend using Resources. It really very helpful laravel feature. Check it out. It is a reusable class. You call anywhere and anytime.
php artisan make:resource UserResource
Go to your the newly created class App/Http/Resources/UserResource.php and drfine the column you want to have in your response.
public function toArray($request) {
return [
"first_name" => $this->first_name,
"middle_name" => $this->middle_name,
"last_name" => $this->last_name
]
}
Now is your controller you can use the UserResource like folow:
public function index()
{
return UserResource::collection(User::all());
}
Or after inserting data you can return the newly added data(f_name, l_name...)
$user = new User;
$user->first_name= $request->first_name;
$user->middle_name= $request->middle_name;
$user->last_name= $request->last_name;
$user->save();
$user_data= new UserResource($user);
return $user_data;

How to update a pivot table using Eloquent in laravel 5

I am new to laravel. I am working on a laravel 5 app and I am stuck here. I have 2 models as such:
class Message extends Eloquent{
public function user()
{
return $this->belongsTo('App\User', 'from');
}
public function users()
{
return $this->belongsToMany('App\User')->withPivot('status');
}
}
class User extends Eloquent {
public function messages()
{
return $this->hasMany('App\Message', 'from');
}
public function receive_messages() {
return $this->belongsToMany('App\Message')->withPivot('status');
}
}
There exist a many-to-many relationship between Message and User giving me a pivot table as such:
Table Name: message_user
Colums:
message_id
user_id
status
I have an SQL query as such:
update message_user
set status = 1
where user_id = 4 and message_id in (select id from messages where message_id = 123)
How can I translate this query to the laravel equivalent?
The code below solved my problem:
$messages = Message::where('message_id', $id)->get();
foreach($messages as $message)
$message->users()->updateExistingPivot($user, array('status' => 1), false);
You may use one of these two functions, sync() attach() and the difference in a nutshell is that Sync will get array as its first argument and sync it with pivot table (remove and add the passed keys in your array) which means if you got 3,2,1 as valued within your junction table, and passed sync with values of, 3,4,2, sync automatically will remove value 1 and add the value 4 for you. where Attach will take single ID value
The GIST: if you want to add extra values to your junction table, pass it as the second argument to sync() like so:
$message = Messages::find(123);
$user = User::find(4);
// using attach() for single message
$user->message()->attach($message->id, [
'status' => 1
]);
$message2 = Messages::find(456); // for testing
// using sync() for multiple messages
$user->message()->sync([
$message->id => [
'status' => 1
],
$message2->id => [
'status' => 1
],
]);
Here is a small example of how to update the pivot table column
$query = Classes::query();
$query = $query->with('trainees')
->where('user_id', Auth::id())
->find($input['classId']);
foreach ($query->trainees as $trainee) {
$trainee->pivot->status = 1 //your column;
$trainee->pivot->save();
}
Note: make sure your relation data must in an array
Hope its help you :)
happy coding
Laravel 5.8
First, allow your pivot columns to be searchable by chaining the withPivot method to your belongsToMany
Copied from my own code to save time
// I have 3 columns in my Pivot table which I use in a many-to-many and one-to-many-through scenarios
$task = $user->goalobjectives()->where(['goal_objective_id'=>$goal_objective_id,'goal_obj_add_id'=>$goal_obj_add_id])->first(); //get the first record
$task->pivot->goal_objective_id = $new; //change your col to a new value
$task->pivot->save(); //save
The caveat is that your pivot table needs to have a primary 'id' key.
If you don't want that then you can try the following:
$tasks=$user->posts()->where(['posts_id'=>$posts_id,'expires'=>true])->get()->pluck('id'); // get a collection of your pivot table data tied to this user
$key=join(",",array_keys($tasks->toArray(),$valueYouWantToRemove));
$tasks->splice($key,1,$newValueYouWantToInsert);
$c = array_fill(0,$tasks->count(),['expires'=>true]); //make an array containing your pivot data
$newArray=$tasks->combine($c) //combine the 2 arrays as keys and values
$user->posts()->sync($newArray); //your pivot table now contains only the values you want
4th July Update Update to above snippet.
//Ideally, you should do a check see if this user is new
//and if he already has data saved in the junction table
//or are we working with a brand new user
$count = $user->goalobjectives->where('pivot.goal_obj_add_id',$request->record)->count();
//if true, we retrieve all the ids in the junction table
//where the additional pivot column matches that which we want to update
if($count) {
$ids = $user->goalobjectives->where('pivot.goal_obj_add_id',$request->record)->pluck('id');
//convert to array
$exists = $ids->toArray();
//if user exists and both saved and input data are exactly the same
//there is no need
//to update and we redirect user back
if(array_sum($inputArray) == array_sum($exists)) {
//redirect user back
}
//else we update junction table with a private function
//called 'attachToUser'
$res = $this->attachToUser($user, $inputArray, $ids, $request->record);
}//end if
elseif(!$count) {
//we are working with a new user
//we build an array. The third pivot column must have equal rows as
//user input array
$fill = array_fill(0,count($inputArray),['goal_obj_add_id'=>$request->record]);
//combine third pivot column with user input
$new = array_combine($inputArray,$fill);
//junction table updated with 'user_id','goal_objective_id','goal_obj_add_id'
$res = $user->goalobjectives()->attach($new);
//redirect user if success
}
//our private function which takes care of updating the pivot table
private function attachToUser(User $user, $userData, $storedData, $record) {
//find the saved data which must not be deleted using intersect method
$intersect = $storedData->intersect($userData);
if($intersect->count()) {
//we reject any data from the user input that already exists in the database
$extra = collect($userData)->reject(function($value,$key)use($intersect){
return in_array($value,$intersect->toArray());
});
//merge the old and new data
$merge = $intersect->merge($extra);
//same as above we build a new input array
$recArray = array_fill(0,$merge->count(),['goal_obj_add_id'=>$record]);
//same as above, combine them and form a new array
$new = $merge->combine($recArray);
//our new array now contains old data that was originally saved
//so we must remove old data linked to this user
// and the pivot record to prevent duplicates
$storedArray = $storedData->toArray();
$user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->detach($storedArray);
//this will save the new array without detaching
//other data previously saved by this user
$res = $user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->syncWithoutDetaching($new);
}//end if
//we are not working with a new user
//but input array is totally different from saved data
//meaning its new data
elseif(!$intersect->count()) {
$recArray = array_fill(0,count($userData),['goal_obj_add_id'=>$record]);
$new = $storedData->combine($recArray);
$res = $user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->syncWithoutDetaching($new);
}
//none of the above we return false
return !!$res;
}//end attachToUser function
This will work for pivot table which doesn't have a primary auto increment id. without a auto increment id, user cannot update,insert,delete any row in the pivot table by accessing it directly.
For Updating your pivot table you can use updateExistingPivot method.

CsvBulkLoader import/update only existing objects

I'm using a simple CsvBulkLoader to bulk update dataobjects.
class OrderImporter extends CsvBulkLoader {
public $delimiter = ';';
public $enclosure = '"';
public $hasHeaderRow = true;
public $columnMap = array(
'ID' => 'ID',
'Bezahlt' => 'Payed',
'Geandert' => 'NeedReview'
);
}
My problem is, that I don't want to create new objects, If they are in the import file. I only want to update the existing ones.
Is there a way to achieve this? Sadly I can't find anything in the docs.
I think you'd have a look at CsvBulkLoader::processRecord(). This is where each line is processed. You could try in your OrderImporter class (untested):
protected function processRecord($record, $columnMap, &$results, $preview = false) {
// find existing object
$existingObj = $this->findExistingObject($record, $columnMap);
return ($existingObject)
? parent::processRecord($record, $columnMap, $results, $preview)
: false;
}
HTH, wmk
You need to set $duplicateChecks based on what fields already existing in your DB can be checked against the import file. If the IDs match you can use:
public $duplicateChecks = array(
'ID' => 'ID',
);
You should test the import on a dev server first, especially if you use a combination of fields, as the results can be different from what you're expecting.
See $duplicateChecks in the BulkLoader api http://api.silverstripe.org/3.1/class-BulkLoader.html

Mass update in Laravel Eloquent or DB

Is there anyone who knows how to do this without the technique of doing it in a one query string. I mean the popular ways I see on the net is by looping in data(the updates) and generating a single update statement and then fire a query. Is it possible for an Eloquent Approach or DB without looping?
This is posible with Eloquent, it might be necessary to enable mass-assignment, but you will get an error if so.
$post_data = Input::all();
$model = Model::find($id);
$model ->fill($post_data);
$model ->save();
or
$post_data = Input::all();
Model::find($id)->update($post_data);
Yes, you can do that but in that case, you have to make the array of data that is a loop is needed to store the data in the array with respective field_name => value of the table.
The following is the example:
$Array = array(); //This is needed to hold data while looping over $YourData
$YourData - is the array of data you want to store in the respective table.
foreach ($YourData as $YourDatakey => $YourDatavalue ){
$Array = [
'table_column_name' => $YourDatavalue['value_from_array'],
'table_column_name' => $YourDatavalue['value_from_array'],
'table_column_name' => $YourDatavalue['value_from_array'],
...... and so on
];
}
$InsertQuery= YourModelName::create($Array);
PS:
YourModelName model file should have the columns in protected
$fillable = ['column1','column2'....];
You should use App\Models\ModelName; at the top of the file.