Proper syntax for Laravel Eloquent insert/update - laravel

I have seemingly simple problem that I still can't solve after days of browsing around.
Say I have Debitur model.
class Debitur extends Model
{
protected $table = 'debiturs';
protected $fillable = [
'name', 'address',
];
public function debiturWife(){
return $this->hasOne('App\DebiturWife');
}
}
And I have a DebiturWife model.
class DebiturWife extends Model
{
protected $table = 'debitur_wifes';
protected $fillable = [
'nama', 'address', 'photo',
];
public function debitur(){
return $this->belongsTo('App\Debitur');
}
}
I can get Debitur and DebiturWife data in the DebiturController like this:
return Debitur::with('debiturWife')->find($debitur_id);
Now I want to create a new Debitur and DebiturWife, how do I do that?
Something like
$debitur = new Debitur;
$debitur->name = $request->name;
$debitur_wife = new Debitur.DebiturWife; //obviously doesn't work

$debitur = new Debitur;
$debitur->name = $request->name;
$debitur->save();
$debitur_wife = new DebiturWife([
'name' => 'Foo'
]);
$debitur->debiturWife()->save($debitur_wife);
Documentation
What if later I want to update DebiturWife based on Debitur's id?
$debitur = Debitur::with('debiturWife')->find($id);
$debitur->debiturWife->name = 'new name';
$debitur->debiturWife->address = 'new address';
$debitur->save();

You could do this way:
$debitur = new Debitur;
$debitur->name = $request->name;
$debitur->save();
$debitur->debiturWife()->create([
'name' => $request->name,
'address' => $request->address,
'photo' => $request->photo
]);
Another way of doing this would be —
$debiturWife = new DebiturWife();
$debiturWife->name = $request->name;
$debiturWife->address = $request->address;
$debiturWife->photo = $request->photo;
$debitur->debiturWife()->save($debiturWife);
The debitur_id will be automatically set. The only difference between the create & save method is that the create method accepts a plain PHP array whereas the save method accepts an instance of DebitureWife
See the documentation for better understanding.

Related

How to save data as array in laravel

I have some of the data, I want to save my data-id in array format. How can I do this? Below is my controller code.
Controller:
public function PostSaveRentCertificateReport(Request $request)
{
$report = $request->session()->get('report');
$reports = new Report;
$reports->column_one = $report->sum('column_one');
$reports->column_two = $report->sum('column_two');
// I want to save those id as array
$reports->adc_report_id = array('$report->id');
$reports->save();
$notification = array(
'message' => 'Report Created Successfully',
'alert-type' => 'success'
);
return redirect()->route('adc.pending.reports')->with($notification);
}
You can encode the data as JSON before saving it.
$reports = new Report;
$reports->column_one = $report->sum('column_one');
$reports->column_two = $report->sum('column_two');
$reports->adc_report_id = json_encode([$report->id]);
$reports->save();
But this will make it a bit more difficult to work with.

Create Relationship inside the create function

I have a model that has a one to many relationship to the versions of the description.
In my Controller
$tag = Tags::create([
'name' => $request->get('name'),
'user_id' => \Auth::id(),
]);
$tag->update([
'content' => $request->get('description')
]);
In my Model:
public function setContentAttribute(string $value)
{
$this->versions()->create([
'user_id' => \Auth::id(),
'value' => $value
]);
}
So I can't put content directly as an attribute in the create method because there is no Model right now.
But is it possible to overwrite the create Method?
When I try to overwrite something like this in my Model it will do an infinity loop
public static function create($attr) {
return parent::create($attr);
}
So my question is if it is possible to have something like this:
$tag = Tags::create([
'name' => $request->get('name'),
'user_id' => \Auth::id(),
'content' => $request->get('content')
]);
and in the Model:
public static function create($attr) {
$value = $attr['content'];
$attr['content'] = null;
$object = parent::create($attr);
$object->content = $value;
$object->save();
return $object;
}
Update
I didn't overwrite the create method but called it customCreate. So there is no infinity loop anymore and I can pass all variables to the customCreate function that handles the relationships for me.
Solution
After reading the changes from 5.3 to 5.4 it turns out that the create method was moved so you don't have to call parent::create() anymore.
The final solution is:
public static function create($attr) {
$content = $attr['content'];
unset($attr['content']);
$element = static::query()->create($attr);
$element->content = $content;
$element->save();
return $element;
}
I don't see why not and you could probably implement a more general approach? Eg. checking if set{property}Attribute() method exists, if it does - use it to assign a value, if it doesn't - use mass assigning.
Something like:
public static function create($attr) {
$indirect = collect($attr)->filter(function($value, $property) {
return method_exists(self::class, 'set' . camel_case($property) . 'Attribute');
});
$entity = parent::create(array_diff_key($attr, $indirect->toArray()));
$indirect->each(function($value, $property) use ($entity) {
$entity->{$property} = $value;
});
$entity->save();
return $entity;
}
I haven't really tested it but it should work. I use something like this in one of my Symfony apps.

Saving Model data to database

I have a Report Model which is like the following.
class Report extends Model
{
protected $table = 'reports';
protected $guarded = [];
public function leadsCollection()
{
return $this->hasMany('App\ReportModels\LeadsCollection');
}
}
A Report can have many LeadsCollection, its Model is the following.
class LeadsCollection extends Model
{
protected $table = 'leadsCollection';
protected $guarded = [];
private $xmlElement;
public function __construct($xmlElement = null, $attributes = array()) {
parent::__construct($attributes);
$this->xmlElement = $xmlElement;
}
public function report()
{
return $this->belongsTo('App\ReportModels\Report');
}
function asArray(){
$reportItem = array();
foreach($this->xmlElement->Leads->Lead as $lead) {
$dateIdentified = date("d/m/Y", strtotime($lead->Date));
$reportItem[] = array(
'LeadID' => (string)$lead->ID,
'Client' => (string)$lead->Client->Name,
'Category' => (string)$lead->Category,
'DateIdentified' => $dateIdentified,
'LeadName' => (string)$lead->Name,
'Owner' => (string)$lead->Owner->Name
);
}
return $reportItem;
}
}
Now I am trying to save some data to a database. So I get a list of all Leads by calling my LeadsCollection and passing it an XML list of Leads.
I then loop these Leads and add it to an array. At the same time however I need to save it to the database. This is what I have so far.
public function getForecastReportForLeads() {
$leads = new LeadsCollection(new \SimpleXMLElement(Helper::getCurrentLeads()));
$reportArray = array();
foreach ($leads->asArray() as $lead) {
$report = new Report();
$report->reportName = 'Lead Forecast';
if($report->save()) {
$leads->leadId = $lead['LeadID'];
$leads->leadCategory = $lead['Category'];
$leads->dateIdentified = $lead['DateIdentified'];
$leads->leadName = $lead['LeadName'];
$leads->owner = $lead['Owner'];
$leads->client = $lead['Client'];
$leads->report_id = $report->id;
$leads->save();
$reportItem = array(
'leadData' => $lead
);
$reportArray[] = $reportItem;
}
}
return $reportArray;
}
So I create the Report item, and within the database if I have 7 Leads I end up with 7 Report rows within my reports table, as it should be. However, when I save the Leads, I only end up with 1 row in my leadsCollection table, every other entry seems to be overridden. I think this is because I am not creating the Lead Object within the loop. However, I cant really create it within the loop because I need to loop whats returned when I first create it.
Not sure how clear I am but is there anything I can add to my Model so I can stop any overriding? Or do I need to do this another way?
Thanks
Either you get the variable inside the save method or initialize the new
$report = new Report($reportItem);
$report->save($report)
I'm having a similar Issue right, let me show my code. It would work for your case. My bug is that I'm updating and the plan_detail.id gets moved instead of creating a new one. But if you create would be fine:
public function store(Request $request)
{
$this->validate($request, [ 'title' => 'required',
'description' => 'required']);
$input = $request->all();
$plan_details = Plan_Detail::ofUser()->get();
$plan = new Plan($request->all());
DB::beginTransaction();
Auth::user()->plans()->save($plan);
try {
foreach ($plan_details as $k => $plan_detail)
Plan::find($plan['id'])->details()->save($plan_detail);
DB::commit();
} catch (Exception $e) {
Log::error("PGSQL plan detail " . $e->message());
DB::rollback();
session()->flash('message', 'Error al guardar el plan de entreno');
}
return redirect('plans');
}

Laravel get relationship model within object after save()

I am using laravel 4.2.
I have two models as below :
class User extends Eloquent{
protected $table = 'users';
public function user_card_details(){
return $this->hasMany('User_card_details');
}
}
And
class User_card_details extends Eloquent {
protected $table = 'user_card_details';
public $timestamps = true;
public $softdeletes = true;
public function user(){
return $this->belongsTo('User')->first();
}
}
And I can save the relationship record using :
$user_card_details = new User_card_details();
$user_card_details->card_number = Input::get('card_number');
$user_card_details->card_exp_month = Input::get('card_expires_m');
$user_card_details->card_exp_year = Input::get('card_expires_y');
$user_card_details->card_cvv = Input::get('card_cvv');
$user->user_card_details()->save($user_card_details);
Up to this it works fine for me.
After save() , I want the user object should be populated with user_details.
So if I want to use the properties, I can use it like :
echo $user->user_card_details->card_number;
But it is not working now.
Any suggestions?
Thanks
You have to remove the () to get the actual model or collection:
echo $user->user_card_details->card_number;
When you're calling the actual function, you'll receive an instance of the Query builder.
Also, it seems that you're not persisting your $user_card_details-object before you try to bind it to your user:
$user_card_details = new User_card_details();
$user_card_details->card_number = Input::get('card_number');
$user_card_details->card_exp_month = Input::get('card_expires_m');
$user_card_details->card_exp_year = Input::get('card_expires_y');
$user_card_details->card_cvv = Input::get('card_cvv');
$user_card_details->save(); //Added this line.
$user->user_card_details()->save($user_card_details);
The more correct way would be:
$user_card_details = [
'card_number' => Input::get( 'card_number' ),
'card_exp_month' => Input::get( 'card_expires_m' ),
'card_exp_year' => Input::get( 'card_expires_y' ),
'card_cvv' => Input::get( 'card_cvv' ),
];
$userCardDetailObj = $user->user_card_details()->create( $user_card_details );
Now, your User_card_detail-instance will be available as the returned object.

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