Saving multiple rows along with Parent table - php

In my app i have a Payers table which hasMany relationship with Spouses in the Model. I want to be able to add several Spouses to a client in situation where they have more than one spouse while saving their details.
For that purpose I created a Livewire component Spouses with code below.This code allows me to add new form group for each Spouse the Payer has.
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class Spouses extends Component
{
public $spouses = [];
public function increments()
{
$this->spouses[] =count($this->spouses)+1;
}
public function remove($index){
unset($this->spouses[$index]);
}
public function render()
{
return view('livewire.spouses');
}
}
The form is on the component page (spouses.blade.php) with the following codes
<div>
<div>
<span wire:click ="increments" class="btn btn-defaults fa fa-plus padding:2 py-1 cursor:pointer text-success"> Click to Add Spouse(s)</span>
</div>
#foreach($spouses as $spouse)
<div class="col-md-10 d-flex">
{!! Form::text('spouse_name', '',array(
'class' => 'form-control col-3',
'id' => 'spouse_name',
'placeholder' => 'Spouse name',
)) !!}
{!! Form::text('spouse_dob', '',array(
'class' => 'form-control col-3',
'id' => 'spouse_dob',
'placeholder' => 'Spouse Age',
)) !!}
{!! Form::text('spouse_emplbiz_add', '',array(
'class' => 'form-control col-3',
'id' => 'spouse_emplbiz_add',
'placeholder' => 'Work Address',
)) !!}
{!! Form::text('spose_income', '',array(
'class' => 'form-control col-3',
'id' => 'spose_income',
'placeholder' => 'Gross Income',
)) !!}
<span class="btn btn-defalt fa fa-times text-danger padding:2"
wire:click="remove({{$loop->index}})"></span>
</div>
#endforeach
</div>
</div>
And finally i imported it to my Payers creation page using the #livewire('spouses').
However after filling the form and adding like three Spouses for each client Only one row is added into the Spouses database.
I will appreciate a careful analysis of my codes to tell me how to ensure that all the spuses record save as the Payers details is saving
My PayerController store method code is shown below
public function store(CreatePayerRequest $request)
{
$input = $request->all();
$payer = $this->payerRepository->create($input);
{
$spouses = new Spouse();
$input = $request->all();
foreach($spouses as $spouse){
$spouses->user_id = Auth::user()->id;
$spouses->payer_id = 1;
$spouses->spouse_occupation='NA';
$spouses->spouse_name = $input['spouse_name'];
$spouses->spouse_dob = $input['spouse_dob'];
$spouses->spouse_emplbiz_add = $input['spouse_emplbiz_add'];
$spouses->spose_income = $input['spose_income'];
$spouses->save();
}
Flash::error('Taxpayers record not saved');
return redirect(route('payers.index'));
Note that i tried moving the line
$spouses = new Spouse();
into the foreach loop but it gave me Undefined variable: spouses

Your foreach loop inside the php script is looping over $spouses, but this variable only holds 1 freshly created Spouse.
Your code is hard to read, since inside your foreach loop you are again using $spouses but with a different meaning.
Your foreach loop should loop over the spouses that come in from your form, so it should loop over for example $request->input('spouce_name') or something.
But this requires array data to come in and I think your form data is not coming in correctly, since inside your blade you create a foreach loop, but you create multiple form elements with the same name. Your form will then only use the last value. If you change the input names to array values, it should work, so change spouce_name to spouce_name[]

Related

Laravel Backpack, Show Address (Json) in Column

I want to show the name field from address json (using algolia), how do i do that?
I tried this:
$this->crud->addColumn([
'name' => "address",
'label' => 'Address',
'type' => 'array',
]);
But can't manage to work. Also, it always return error.
In preview, i want to show full address too, but only got json return.
Thanks in advance
Managed to work with Custom Fields.
Controller:
$this->crud->addColumn([
'name' => 'address', //db field
'label' => "Address",
'type' => 'customAddress' //name of custom created custom field
]);
Create a custom field at: resources\views\vendor\backpack\crud\columns
I named it as 'customAddress.blade.php'
Custom Field:
{{-- customAddress--}}
<?php $object = (object) json_decode($entry['address'], true); ?>
<span><?php echo $object->value; ?></span>
Also you can display with foreach, something like $object->name, $object->country, etc...
Hope this helps someone.
Edit 1 - Backpack 4.1 updated column
For everyone who are migrating from Backpack 4.0 to 4.1 you should update this custom column too.
Here is an updated code for Backpack v4.1.
In v4.1 there is a difference if column is escaped or not.
I also added a check in case of a JSON parsing error.
{{-- customAddress--}}
#php
$value = data_get($entry, $column['name']);
$column['escaped'] = $column['escaped'] ?? false;
json_decode($entry['address']);
if(json_last_error() == JSON_ERROR_NONE){
$object = (object) json_decode($entry['address'], true);
$value = $object->value;
} else{
$value = $entry['address'];
}
#endphp
<span>
<span class="d-inline-flex">
#includeWhen(!empty($column['wrapper']), 'crud::columns.inc.wrapper_start')
#if($column['escaped'])
{{ $value }}
#else
{!! $value !!}
#endif
#includeWhen(!empty($column['wrapper']), 'crud::columns.inc.wrapper_end')
</span>
</span>
Hope this helps someone who is migrating from Backpack 4.0.

Laravel 5.5 correct way for a CRUD form select

So I have this CRUD where I use the same form to create and edit entries.
I need in several form selects, when creating (no data present for that particular field yet) my select to show the placeholder, but when editing, my select to show whatever is stored on database for that particular id filed. So I have:
Controller:
...
public function create()
{
$house = houses::pluck('name', 'id');
//$thisclient = null;
$clients = client::pluck('last_name', 'id');
$reps = user::where('role_id', 5)->orderBy('first_name')->get()->pluck('full_name', 'id');
return view('prospects.create', compact('house', 'clients', 'reps'));
}
...
public function edit($id)
{
$house = houses::pluck('name', 'id');
//$thisclient = user::whereId($id)->first();
$clients = client::pluck('last_name', 'id');
$reps = user::where('role_id', 5)->orderBy('first_name')->get()->pluck('full_name', 'id');
$prospect = Prospect::findOrFail($id);
return view('prospects.edit', compact('prospect', 'house', 'clients', 'reps'));
}
and my view form:
Working for create:
{!!Form::select('client_id', $clients, null, ['class' => 'form-control', 'placeholder' => 'Please Select'] ) !!}
Working for edit:
{!! Form::select('client_id', $clients, $prospect->client_id, ['class' => 'form-control'] ) !!}
I'm having 2 troubles here, if I have null as my selected field, it won't bring the selected data on edit, if I have $prospect->client_id , it will return a error on create as there's no data yet.
I tried to solve this by creating a variable $thishouse on controller and passing it to view on return view('prospects.create', compact('house', 'thisclient','clients', 'reps')); and view Form::select('client_id', $clients, $thisclient, ['class' => 'form-control'] ) !!} but seems a bit dirty whne having several form selects...
The second trouble is if I leave a placeholder on Edit, it will show the placeholder, not $prospect->client_id itself.
What's the best and simplest way to achieve all of this and use the same form for create and edit?
Thanks
You can use Form::open and Form::model to create and edit. As an example, you can set in your view:
#if(isset($prospect))
{!! Form::model($prospect, ['action' => ['ProspectController#update', $prospect->id], 'method' => 'patch']) !!}
#else
{!! Form::open(array('action' => 'ProspectController#store', 'method' => 'POST')) !!}
#endif
And then you can create the select like this:
{!! Form::select('client_id', $clients, old('client_id'), ['class' => 'form-control'] ) !!}
So, when you are editing, Laravel will select the attribute from the variable on model function.
And since you are using Laravel 5.5, you could also use #isset instruction.

User Submitted Posts Store function using Laravel

So in addition to my prior topic (thank you guys for helping) routes with compact
I am now facing troubles with my store function, I am getting the error : "Trying to get property of non-object.
This is my store function in my Controller
public function store(Request $request)
{
// validate the data
$this->validate($request, array(
'title' => 'required|max:255',
'body' => 'required'
));
// store in the database
$userpost = new Usp;
$userpost->title = $request->title;
$userpost->body = $request->body;
$userpost->save();
Session::flash('success', 'The blog post was successfully saved!');
return redirect()->route('admin.userposts.show', $userpost->id);
}
This is the view to create userpost(to make it more clear, p.s. the same form with different route ofcourse, works for my simple posts but not for my user submitted posts)
#extends('index')
#section('index-stylesheets')
{!! Html::style('css/parsley.css') !!}
#endsection
#section('content')
<h1>Create New User Post</h1>
<hr>
{!! Form::open(array('route' => 'admin.userposts.store', 'data-parsley-validate' => '')) !!}
{{ Form::label('title', 'Title:') }}
{{ Form::text('title', null, array('class' => 'form-control', 'required' => '', 'maxlength' => '255')) }}
{{ Form::label('body', "Post Body:") }}
{{ Form::textarea('body', null, array('class' => 'form-control', 'required' => '')) }}
{{ Form::submit('Create Post', array('class' => 'btn btn-success btn-lg btn-block', 'style' => 'margin-top: 20px;')) }}
{!! Form::close() !!}
#endsection
#section('index-scripts')
{!! Html::script('js/parsley.min.js') !!}
#endsection
Method of showing the post:
public function show($id)
{
$userpost = Usp::find($id);
return view('admin.userposts.show', compact('userpost'));
}
So the fact is that the problem was not the store method but the routes.
Route::get('/userposts/{id}', 'UserPostsController#show')->name('admin.userposts.show');
Route::get('/userposts/create', 'UserPostsController#create')->name('admin.userposts.create');
Route::post('/userposts/store', 'UserPostsController#store')->name('admin.userposts.store');
When registering the routes in that order, when laravel will iterate over your routes, it will first encounter the show one, and it will therefore take "create" as the id. Therefore, it will go into the show method and it won't find any post that matches, the post being null, you get the error.
So, there are two ways of fixing this.
The first one (the easiest, works in all cases, maybe not the best) is to put the create route before the show route.
The second one, the best in my opinion, is to add a condition to the id (doesn't work in the case of a slug). As the ids are only integers, you get :
Route::get('/userposts/{id}', 'UserPostsController#show')->name('admin.userposts.show')->where('id', '[0-9]+');
Therefore, create won't match the regular expression and it won't go in the show method.
For "resource creations" (storing in database), I wouldn't use a "field-by-field" method.
Instead, I'd do something like this :
$userpost = Usp::create($request->only('title', 'body'));
I feel this is more talkative.
But, it won't work, laravel protects* us against such things. To make it work, you have two options.
(The best option in my opinion)
In your model, add a protected variable called $fillable with all your columns that you allow to mass assign*. In this case you would put :
protected $fillable = ['name'];
(The option if you are sure of what are you doing)
In your model, you can say, hey, I know what I'm doing here, just let me do my stuff without guarding me. In this case you would put :
protected $guarded = [];
Notes :
$request->only('field1', ...) gives you an array of the fields that you want with the fields name as keys, in this case it gives you ['field1' => $request->field1]. In your case it will give you ['title' => $request->title, 'body' => $request->body].
Mass assignment is when you give an array to the model and it puts all attributes to the fields of the array. More informations here https://laravel.com/docs/5.4/eloquent#mass-assignment
When I mean laravel protects us against those things, it does't really protect us because it isn't a bad practice (instead, I find it more readable), but because it does allow you to make mistakes (for exemple, setting fields that don't exist).

Laravel 5 | Select to variable

I've created a very simple form that has some select values and when you select one of the items from the list and hit submit, it goes to another page that imports a file template to confirm your selection. For some reason on the next page, instead of displaying the item name that was selected, only the row ID of the select value pops up. Is there an additional option that I need to pass through to get the value displayed in the list?
Here's my create.blade.php
<div class="form-group">
{!! Form::label('candy_flavors', 'Candy Flavors:') !!}
{!! Form::select('candy_flavors', array('' => 'Select Flavor') + $candy, null, ['class' => 'form-control'])!!}
</div>
Here's my CandyController.Php
public function create()
{
$candy = Candy::all()->lists('name');
return view('candy.create', compact ('candy'));
}
public function confirm(Requests\PrepareCandyRequest $request, Guard $auth)
{
$candytemplate = $this->compileCandyRequestTemplate($request->all(), $auth);
return view('candy.confirm', compact('candytemplate'));
}
public function compileCandyRequestTemplate($data, Guard $auth)
{
$data = $data + [
'name' => $auth->user()->name,
'email' => $auth->user()->email,
];
return view()->file(app_path('Http/Templates/candytemplate.blade.php'), $data);
}
Here's my candytemplate.blade.php
#extends ('master')
#section ('content')
This is your candy selection: {{ $candy }}
#endsection
In confirm you can just call:
$request->get('candy_flavors')
However it will return blank, because it is attempting to return you the "value", not the "display value" of the select box. And in your case you are passing in an array of empty keys. array('' => 'Please select', '' => 'name 1', etc).
The form builder class uses the keys of the array to fill in the values.
$candy=['apple'=>'apple','banana'=>'banana','mango'=>'mango'];
and now use
<div class="form-group">
{!! Form::label('candy_flavors', 'Candy Flavors:') !!}
{!! Form::select('candy_flavors', array('' => 'Select Flavor') + $candy, null, ['class' => 'form-control'])!!}
</div>
you have to specify key and value otherwise select will set value from 0
So what I decided to do was make the call from my controller, and pass through the label twice. This did the trick
$accountexecutive=AccountExecutive::lists('flavor', 'flavor');
return view('candy.create', compact ('flavors'));

Laravel form model binding for many to many select input

I have a many to many relationship between clients and assets.
For my edit client form I have multi-select box with which the user can select several assets to attach to a client. The form is using model-binding so it automatically populates the fields with the existing client data.
The model binding works for all fields in the form except the multi-select. Here is a snippet from my view:
{{ Form::model($client, ['route' => ['clients.update', $client->id], 'class' => '', 'method' => 'put']) }}
{{ Form::label('name', 'Name', $label_attributes) }}
{{ Form::text('name', null, array('class'=>'form-control')) }}
{{ Form::label('assets', 'Client Benchmarks (Select multiple)', $label_attributes) }}
{{ Form::select('assets[]', $assets, null, array('multiple' => true, 'class' => 'form-control')); }}
When I submit the form the relationship saves the data successfully via the sync method in my controller (update method):
$client = Client::find($id);
$client->name = Input::get('name');
$assets = Input::has('assets') ? Input::get('assets') : array();
$client->assets()->sync($assets);
$client->save();
Also, if I output the client assets directly they are accessible in the model collection in my form view:
<?php print_r($client->assets); ?>
How can I get the form to populate the select box with the existing selections?
echo Form::select('assets[]', $assets, array(1,2), array('multiple' => true));
As far as I know it won't set all selected values by default for you even when using form
model binding
This is what the native formBuilder method looks like:
public function select($name, $list = array(), $selected = null, $options = array())

Categories