I have a form that contains a list of settings, all with their own IDs, shown via simple foreach loop. When the user clicks the update button, I want to be able to update all of the values using Anahkiasen/Former. However, I can't seem to find the documentation on how to submit more than one set of records.
For instance, if I was opening the form to edit just one setting instead of all of them, I would just do the following:
{{ Former::open()->method('PUT')->action(URL::to('settings/' . $setting['id'])) }}
This would send it to my 'update' resource method and the following code would take care of the update:
public function update($id)
{
$setting = Setting::findOrFail($id);
if ($setting->save()) {
return Redirect::back()->with('message', "Setting #$id updated!");
} else {
return Redirect::back()->withInput()->withErrors($setting->errors());
}
}
Is it possible to do this with multiple records at once and still use the convenient $autoHydrateEntityFromInput and $forceEntityHydrationFromInput features?
I'm using Laravel 4.1 and Former 3.4.2.
This had to be done by just putting the values into an array and forcing the existing value in the Blade template:
#foreach ($settings as $s)
<div class="row">
{{ Former::text('values[' . $s['id'] . ']', $s['name'])->forceValue($s['value']) }}
</div>
#endforeach
Then retrieve the values in the controller:
public function update()
{
$values = Input::get('values');
foreach ($values as $id => $val) {
$setting = Setting::findOrFail($id);
$setting['value'] = $val;
if (!$setting->save()) {
return Redirect::back()->withInput()->withErrors($setting->errors());
}
}
return Redirect::back()->with('message', "Settings updated!");
}
Doing it this way, a standard resource won't work like Route::resource('settings', 'SettingsController'); so instead I had to simply do Route::post('settings/update', 'SettingsController#update');
As far as I can tell, auto hydration also won't work in this case.
Related
I have a weird problem with Livewire, I am unable to identify why this is happening.
The Problem is: I have a list populated from foreach loop, and the data in foreach loop comes from the search function. For example, I have two list items on the list, and I update some data on them using the dropdown available, if i update the first item and recall the search function the data is updated and now only one item left in the list but now it is with wrong wire model id, but the data come from search function is correct but livewire assign the wrong id to it.
To understand the issue properly, please watch this short video.
Relevant Codes:
Blade
<div>
#foreach ($this->result as $data)
<div>
<span class="has-float-label" style="width: 190px;">
<select wire:model="list_session_picker.id.{{ $data->id }}" wire:key="sessionList-{{ $data->id }}" required class="form-control">
#foreach ($sessionData as $session)
{{ $this->isDetailsExist ? ($this->list_session_picker['id'][$data->id] = "$data->student_session_id") : null }} {{-- This line of code selects the default value of the item in the database, so t will be sow as selected --}}
<option value="{{ $session->id }}">{{ $session->session }}</option>
#endforeach
</select>
</span>
</div>
</div>
<div>
<button class="btn btn-secondary text-light" wire:click.prevent="promote">
Promote all
</button>
</div>
Componenet
public $result = [], $list_session_picker = [], $selectedItems = [];
public $isDetailsExist = true;
public function search()
{
$search = SchoolStudent::query();
if (!empty($this->search_session_picker && $this->search_class_picker && $this->search_medium_picker)) {
$search->where('student_session_id', $this->search_session_picker);
$search->where('student_class_id', $this->search_class_picker);
$search->where('student_medium', $this->search_medium_picker);
$this->result = $search
->whereNull('date_of_graduate')
->with(['session:id,session', 'class:id,name'])
->get();
}
}
public function resetFields()
{
$this->selectedItems = [];
$this->list_session_picker = [];
$this->result = [];
$this->isDetailsExist = true;
}
public function promote()
{
if (count($this->selectedItems) > 0) {
foreach ($this->selectedItems as $ikey => $sitems) {
if (isset($this->selectedItems[$ikey]) && $this->selectedItems[$ikey] !== false) {
foreach ($this->list_session_picker as $lskey => $session) {
$selectedStudent = SchoolStudent::findOrFail($sitems);
$selectedStudent->student_session_id = $this->list_session_picker[$lskey][$selectedStudent->id];
$selectedStudent->update();
}
unset($session);
}
}
unset($sitems);
$this->resetFields();
$this->search();
}
}
To refresh the list I am calling the search method again with already selected values, but is there any other way please suggest, because if I am not doing the search again, dom is all correct but the updated item has not been removed from the list, but I prefer to call the search function again
Since my comment seems to have solved the issue, I'll leave it as answer so it can be accepted.
Within the video, the dropdown is wrapped in a wire:ignore. This prevents the dropdown from ever getting updated.
Read more about it in the docs.
I'm using a laravel custom helper to print data in my view blade file. I pass a foreign key from a table to the custom helper and the custom helper checks the foreign key and returns its associated data. Recently my colleage suggested it is a bad idea to use a custom helper in a foreach loop of a view file. I didn't experience any performance issues, however my colleague suggested that when uploading to an AWS server, each request to the database would be charged. This is my code:
<?php
foreach($data as $key => $details) { ?>
<td>{{ helper::getstatename($details>stateid)}}</td>
<? }
?>
This is the helper file:
public static function getstatename($id) {
$getstatename = states::select('statename','id')->where('id',$id)->first();
if($getstatename){
return $getstatename->statename != '' ? $getstatename->statename : '';
} else {
return false;
}
}
My controller:
public function index() {
$locations = DB::table('locations')->select('locations.id','locations.locationname','cityid','stateid','countryid','locations.status')
->where('locations.status','!=',2)->orderBy('id','desc')->get();
return view('location/listlocation',['data'=>$locations]);
}
So I want to know, is this practice of using a helper file bad? Or should I stick to using a left join and print that data?
better you use joins to get state name at the $locations in the controller itself
public function index2(){
$s_books = raws::orderBy('sequence', 'ASC')->where('type',"book")->get();
foreach ($s_books as $data) {
$menu="{$data->menu}";
$main_books = books::orderBy('id', 'DESC')->where("menu",$menu)->get();
}
return view('index')->with(['main_books'=>$main_books]);}
And this is Blade
#foreach($main_books as $data)
{{$data->id}}
#endforeach
But this is showing only the last data.
How can I show all tha data in $main_book in laravel blade???
The code you have, you will get only the last item, you need to put all in an array. Try this code in you controller.
$main_books = [];
foreach ($s_books as $data) {
$menu="{$data->menu}";
array_push($main_books, books::orderBy('id', 'DESC')->where("menu",$menu)->get());
}
I'm creating a UI in my application which will allow the user to decide the state of received content, this includes updating it. How does CI handle this?
I've tried the different update methods provided in the query builder part of the documentation, including replace and update, I pass on the data from the view to the controller, to the model in the form of an array. Yet still, when I try it, it creates a new row with that single value and with all other columns empty.
view.php
<form action="Application/Update" method="get">
<input type="hidden" name="mar-id" value="<?php echo $row['id']; ?>">
<input type="hidden" name="mar-read" value="New-value">
<?php echo anchor('http://localhost/dir/dir/dir/index.php/Application/Update', 'update'); ?>
</form>
controller.php
public function Update() {
$this->load->helper('url');
$this->load->model('Main');
$id = $this->input->post('mar-id');
$value = $this->input->post('mar-read');
$mar = $this->Main->Update($id, $value);
if ($mar == TRUE) {
redirect('http://localhost/dir/dir/dir/index.php/Application/Otherpage', 'refresh');
}
else {
redirect('http://localhost/dir/dir/dir/index.php/Application/Otherpage');
}
}
model.php
public function Update($id, $value) {
$data = array(
'status' => $value
);
$this->db->where('id', $id);
$update = $this->db->update('table', $data);
}
As I said, I expect the row to be updated based on the row-id provided. Instead it creates a completely new row with that single value. It doesn't return any error messages though.
There are a number of mistakes here.
SO to date we have established that performing var_dumps in the controller results in NULL for all your "POST" values.
I've assumed the following for simplicity.
Controller Name is: Program.php (Application is NOT an Allowed controller name as it's a foldername)
Model Name is: Mdl_update.php
View is: update_view.php
Issue #1:
Your Form has an issue where you are using an anchor tag which is just a link. It does nothing in submitting any data from the form.
So we have to remove the Anchor Tag and replace it with a Form Submit. You have to Submit the form to get any chance of sending the form data.
For testing your GET and POST I've added in Two different Forms.
In update_view.php
<!-- Set the Method to GET -->
<form action="program/update" method="get">
<input type="hidden" name="mar-id" value="<?php echo $row['id']; ?>">
<input type="hidden" name="mar-read" value="New-value">
<input type = "submit" name="update" value="Update with GET">
</form>
<!-- Set the Method to POST as this is what the Controller is Expecting -->
<form action="program/update" method="post">
<input type="hidden" name="mar-id" value="<?php echo $row['id']; ?>">
<input type="hidden" name="mar-read" value="New-value">
<input type = "submit" name="update" value="Update with POST">
</form>
What I used to display the Form in the controller by simply calling the program/index in the Program controller.
public function index() {
$this->load->helper('url');
$data['row'] = array('id' => 2);
$data = $this->load->view('update_view', $data, TRUE);
echo $data;
}
So your Controller is looking for POST and not GET. This can be proven by changing the controller up a bit for debugging.
public function update() {
$this->load->helper('url');
$this->load->model('mdl_update');
$id = $this->input->post('mar-id');
$value = $this->input->post('mar-read');
echo '<h2>POST Values</h2>';
var_dump($id);
var_dump($value);
// ****************************
// These are added in for debugging/Demonstration to show values for the form using the GET method.
$id_get = $this->input->get('mar-id');
$value_get = $this->input->get('mar-read');
echo '<h2>GET Values</h2>';
var_dump($id_get);
var_dump($value_get);
// ****************************
exit('Stopped for Debugging: Method '. __METHOD__.' at Line: '.__LINE__); // Added for Debug
$mar = $this->mdl_update->Update($id, $value);
if ($mar == TRUE) {
redirect(base_url('program/otherpage'), 'refresh');
} else {
redirect(base_url('program/otherpage'));
}
}
So you are looking for POST Data when your form method is set to GET. Please be aware of what you are setting. They must match.
If you want to use GET, you need to use $this->input->get()
The code above will let you test both.
So you now have a POST and GET Form and the controller is setup to demonstrate the two different types. Choose Either GET or POST!. That is up to you on which one you choose.
Issue #2: Expecting a return value from your Model when you are not returning anything.
In your Controller you have the line...
$mar = $this->mdl_update->Update($id, $value);
And in your Model you have...
public function update ($id,$value) {
$data = array(
'status' => $value
);
$this->db->where('id', $id);
$this->db->update('db_table', $data);
}
Your Model Method is not returning anything.
You should always look up what your return values are. I am expecting that your intention was to return the value of the update. Looking through the CI Code itself it appears that if things go wrong it will return FALSE (if the database debug is disabled - learnt something new)
I've added in some debug to assist in viewing what is going on here.
public function update($id, $value) {
$data = array(
'status' => $value
);
$this->db->where('id', $id);
$update_result = $this->db->update('db_table', $data);
echo $this->db->last_query(); // Added for DEBUG
return $update_result;
}
Now I cannot get your code to create new rows as you claim. It's impossible, with this code, to add new rows. So thats happening from something you haven't shown us but that is an aside and not important here.
If we alter the controller to view the model etc (I am only showing the changes ) we would change
exit('Stopped for Debugging: Method '. __METHOD__.' at Line: '.__LINE__);
$mar = $this->mdl_update->Update($id, $value);
To this
$mar = $this->mdl_update->Update($id, $value);
var_dump($mar);
exit('Stopped for Debugging: Method '. __METHOD__.' at Line: '.__LINE__);
If you run this and submit either the GET ( Results are NULL ) or POST, the update will always return TRUE. So your redirect etc needs to be looked at on how you decide on one or the other.
I think you should set your table columns to not allow them to be NULL AND add in some "Validation" in your controller.
ISSUE 3: No Form Validation
CodeIgniter has a Form Validation Class that I suggest you read. This is getting way too long to go into that here...
So as you go through this, you can add/remove debugging to test what is going on and progress it along the way as I have hopefully shown.
if anything is unclear, just ask. I'm sure I may have left something out.
what i am trying to do is getting my tvserious and movies from categories class
this is my categories class :
class Category extends Model
{
public function movies()
{
return $this->hasMany(Movie::class);
}
public function tvserious()
{
return $this->hasMany(Tvserious::class);
}
what i tried and it's working
public function CategoryClick($slug){
$media = Category::where('slugid',$slug)->with(['movies' => function($query) {
$query->whereNotNull('title');
},'tvserious' => function($query) {
$query->whereNotNull('title');
}])->inRandomOrder()->paginate(8);
return view('test')->with([
'catclick'=>$media,
'title'=>$slug,
]);
}
the problem with this way is in my blade i have to create a loop for movies and tvserious and the tvserious data will always stay at the end at it will show after the movies loop ends
#foreach($catclick as $media)
#foreach($media->movies as $movie )
{{ $movie->title }}
#endforeach
#foreach($media->tvserious as $tvserious )
{{ $tvserious->title }}
#endforeach
#endforeach
so how can i get both of my movies and serious in my blade mixed together
i don't want all the movies to be at first so where is the problem and how can i fix this ?
this worked pretty well for me ... #first
i made an accessor in my category class
public function getMediaAttribute()
{
return $this->movies->values()->toBase()
->merge($this->tvserious->values())
->sortByDesc(function ($media, $key) {
return $media->updated_at;
});
}
then in my controller
this is my controller function
use Illuminate\Pagination\LengthAwarePaginator as Paginator;
public function CategoryClick($slug){
$all = Category::where('slugid',$slug)->first()->getMediaAttribute(); // calling the accessor
// Then use following code to paginate the results:
$perPage = 10;
$currentPage = app('request')->get('page') ?: 1; // or $request->get('page') if available
$paginator = new Paginator($all, $all->count(), $perPage, $currentPage);
return (dd($paginator));
everything is great now thx for everyone who helped me to solve thix issue :D
Add an accessor to your Category model:
class Category
{
public function getMediaAttribute()
{
return $this->movies->values()->toBase()
->merge($this->tvserious->values())
->sortByDesc(function ($media, $key) {
return $media->updated_at;
});
}
}
Here we used a base collection instead of the Eloquent collection due to merge issues with the Eloquent collection (it does not allow for duplicate keys, thanks #lagbox for pointing it out). After merging both media types together, we also sort the resulting list based on their last update. This means recently touched entries will be listed first.
The newly created list can be accessed with $category->media. Instead of sortByDesc($func) also a simple sortBy('column') or shuffle() (to get a random order) would work.
You can use this code
#php
$moviesCount = $media->movies->count();
$tvseriousCount = $media->tvserious->count();
$maxCount = ($tvseriousCount > $moviesCount) ? $tvseriousCount : $moviesCount;
#endphp
#for ($index = 0; $index < $maxCount; $index++)
#isset($media->movies[$index])
{{ $media->movies[$index]->title }}
#endisset
#isset($media->tvserious[$index])
{{ $media->tvserious[$index]->title }}
#endisset
#endfor