Backpack Laravel Admin Incorrectly Redirects after Editing Model - laravel

I have a CrudController created for a model using the Backpack Laravel Admin Library.
When I update the model it redirects me incorrectly to a 404 page with the message No query results for model [App\Models\Group].
It is redirecting me to the incorrect URL from what I can tell.
admin/group/261/ instead of admin/group/261/edit
The model also does not update.
I have the "Save and Edit" option set on the green save button. If I try to change this I get the same error, but it doesn't update.
I'm able to save every other model correctly.
The update method in the CrudController is just the following. I've removed all the extra code.
public function update(){
$response = $this->traitUpdate();
return $response;
}

Figured this out. It was because I was referencing the Primary Key -> 'id' in the fields within the Group Crud Controller.
$this->crud->addField([
'name' => 'id',
'type' => 'text',
'attributes' => ['disabled' => 'disabled'],
]);

u can use id, u need delete attribute 'disabled' like this:
[
'name' => 'id',
'label' => 'ID',
'attributes' => [
'readonly' => 'readonly',
],
],

Related

In listing page of users in the column of action want add a change_password link and its process of working

I am using laravel-backpack 4.0. want to add a password change link with a page and all the functionality with validation and all, to a listing of users like edit, in the Action column.
It seems a bit odd to want another page where you can update only the password when the package's built in user crud lets you update that, and all the other user fields. That said, assuming you have your reasons (and that I've understood the usage correctly), one approach would be to use the users addon package suggested but then make a second CRUD controller for the user model that only supports the "update" operation and only allows editing the password.
NOTE
This is untested so there might be some minor issues to iron out, but the approach is sound.
Install and configure the users addon package. Then, create a second controller for users but edit such that only the "update" action is allowed and only the password and password confirmation fields are editable. We'll make the name and email read only so you can see who its for but cant edit those fields. You can make those fields hidden if you want, or remove them, but if you remove them, note that you'll need to create a custom request class and update the rules to not require those fields on submission.
<?php
namespace App\Http\Controllers;
use Backpack\CRUD\app\Http\Controllers\CrudController;
use Backpack\CRUD\app\Http\Requests\CrudRequest;
use EduardoArandaH\UserManager\app\Http\Requests\UserStoreCrudRequest as StoreRequest;
use EduardoArandaH\UserManager\app\Http\Requests\UserUpdateCrudRequest as UpdateRequest;
class UserPasswordCrudController extends CrudController
{
use \Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation { update as traitUpdate; }
public function setup()
{
$this->crud->setModel(config('backpack.permissionmanager.models.user'));
$this->crud->setEntityNameStrings('User Password', 'User Passwords');
$this->crud->setRoute(backpack_url('userPasswords'));
$this->crud->denyAccess('create');
$this->crud->denyAccess('list');
$this->crud->denyAccess('delete');
$this->crud->denyAccess('reorder');
$this->crud->denyAccess('revisions');
}
public function setupUpdateOperation()
{
$this->addUserFields();
$this->crud->setValidation(UpdateRequest::class);
}
/**
* Update the specified resource in the database.
*
* #return \Illuminate\Http\RedirectResponse
*/
public function update()
{
$this->crud->setRequest($this->crud->validateRequest());
$this->crud->setRequest($this->handlePasswordInput($this->crud->getRequest()));
$this->crud->unsetValidation(); // validation has already been run
return $this->traitUpdate();
}
/**
* Handle password input fields.
*/
protected function handlePasswordInput($request)
{
// Remove fields not present on the user.
$request->request->remove('password_confirmation');
$request->request->remove('roles_show');
$request->request->remove('permissions_show');
// Encrypt password if specified.
if ($request->input('password')) {
$request->request->set('password', Hash::make($request->input('password')));
} else {
$request->request->remove('password');
}
return $request;
}
protected function addUserFields()
{
$this->crud->addFields([
[
'name' => 'name',
'label' => trans('backpack::permissionmanager.name'),
'type' => 'text',
'attributes' => ['readonly' => 'readonly'],
],
[
'name' => 'email',
'label' => trans('backpack::permissionmanager.email'),
'type' => 'email',
'attributes' => ['readonly' => 'readonly'],
],
[
'name' => 'password',
'label' => trans('backpack::permissionmanager.password'),
'type' => 'password',
],
[
'name' => 'password_confirmation',
'label' => trans('backpack::permissionmanager.password_confirmation'),
'type' => 'password',
],
]);
}
}
Load the route for the new controller:
<?php
Route::group([
'namespace' => 'App\Http\Controllers',
'prefix' => config('backpack.base.route_prefix', 'admin'),
'middleware' => ['web', backpack_middleware()],
], function () {
Route::crud('userPasswords', 'UserPasswordCrudController');
});
Create a custom button at resources/views/vendor/backpack/crud/buttons/update_password.blade.php with this content:
#if ($crud->hasAccess('update'))
<!-- Single edit button -->
<i class="la la-edit"></i>Edit Password
#endif
Finally, in your normal user crud controller (or whatever controller you want the button in) add the button to the line stack in your controller's setupListOperation method:
public function setupListOperation()
{
$this->crud->addButtonFromView('line', 'update_password', 'view', 'end');
// ... normal setup code
}

Add a custom item to eloquent collection

I am using Laravel 7.
I have Category model. I sent categories as API with laravel resources. But now I want to add "all" value to categories.
Controller:
'categories' => CategoryResource::collection(Category::all()->push([
'id' => 0,
'name' => "All",
'subcategories' => []
]))
Resource:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'subcategories' => SubcategryResource::collection($this->subcategories)
];
}
Also, I wanted to add this custom value to the beginning of the collection.
But I am getting this error:
Trying to get property 'id' of non-object
Please, help me. How can I solve my problem?
Try replacing $this->id with $this['id']
If that works your query is returning an array not an object.
The problem here is that you're trying to add an item to the collection which is not a category model. $this in the resource reverts to the given model. However, you pass in an array so there is no model.
So try this instead
$allCategory = new Category(['id' => 0, 'name' => 'All']);
'categories' => CategoryResource::collection(Category::all()->prepend($allCategory));
I got these from: https://laracasts.com/discuss/channels/eloquent/add-a-custom-value-to-eloquent-collection

Two fields overwriting each others values on saving in Backpack for Laravel CRUD (spatie/laravel-tags implementation issues)?

I am trying to use spatie/laravel-tags together with Backpack for Laravel. I have 2 types of tags defined. Currently I have extended the Tag model from spatie/laravel-tags as MyCategory and MyTag and added global scopes to separate the two tag types. This works to the extent that it will display the current categories and tags correctly in Backpack, but when I try to save any changes it will only save the entries in the last field, and delete everything in the first field.
Here is my current field configuration for my CRUD:
$this->crud->addField([
'name' => 'categories',
'label' => 'Categories',
'type' => 'select2_multiple',
'tab' => 'Overview',
'attribute' => 'name',
'model' => 'App\MyCategory',
'pivot' => true,
]);
$this->crud->addField([
'name' => 'tags',
'label' => 'Tags',
'type' => 'select2_multiple',
'tab' => 'Overview',
'attribute' => 'name',
'model' => 'App\MyTag',
'pivot' => true,
]);
When I check Laravel Telescope I see that the same thing happens for both fields. First all current tags (regardless of type) for the item I am saving are deleted, and the new tags from the field are added. This is then repeated for the second field, which of course deletes the tags from the first field that should also be kept.
It seems that GlobalScope on my extended Tag models does not stick around for this part. Is there any way to reintroduce the scopes into the queries run by backpack to get these tags to save correctly?
In my CrudController I created custom update and store functions. See the update example below. This seems to work fine. There are still 2 queries being run, with the second one undoing the first one, but for my purposes this is a good enough workaround to be able to have 2 fields with different tag types in the same form in Backpack, using spatie/laravel-tags.
public function update(UpdateRequest $request)
{
$request = request();
// Merge the values from the two tag fields together into the second field
$request->merge(['tags' => array_merge((array)$request->input('categories'), (array)$request->input('tags'))]);
$redirect_location = $this->traitUpdate($request);
return $redirect_location;
}

Laravel backpack addField where clause

Using this link.
I tried to add a where clause in addField where the dropdown should load the contents as options only if they match a condition.
Here is the code:
$this->crud->addField([ // select_from_array
'name' => 'manager',
'label' => "Manager Name",
'type' => 'select_callback', // Custom field type of select2
'entity' => 'Manager',
'attribute' => 'name',
'model' => 'App\Models\Manager',
'scope' => 'manager'
// 'allows_multiple' => true, // OPTIONAL; needs you to cast this to array in your model;
], 'update/create/both');
And in Model.php
public function scopeManager($query)
{
return $query->where('gym_code', Auth::user()->gym_code);
}
But it is not working!!
Thanks
I found the answer in the same link that I suggested in Question.
In the link #thplat answered a change in select_callback.blade.php
For the select_callback view I took the select view and changed only one line there.
We go from:
#foreach ($field['model']::all() as $connected_entity_entry) to #foreach ((isset($field['callback']) ? $field['callback']() : $field['model']::all()) as $connected_entity_entry).

How to properly hydrate and extract Doctrine Entities from Zend Forms

I'm just starting out with Doctrine and was rewriting some code to use Doctrine entities in some Forms.
I have an Entity Business which has some 1:n relations with addresses, employees, emails etc. the Setup is really basic and working fine.
To add new Businesses i created a BusinessForm and Fieldsets for each of my entities. Here the constructor of the form:
public function __construct($scenario='create', $entityManager = null) {
parent::__construct('business_form');
$this->scenario = $scenario;
$this->entityManager = $entityManager;
$this->setAttribute('method', 'post');
$businessFieldset = new BusinessFieldset($this->entityManager);
$businessFieldset->setUseAsBaseFieldset(true);
$this->add($businessFieldset);
$hydrator = new DoctrineHydrator($this->entityManager, new Business());
$this->setHydrator($hydrator);
$this->addElements();
$this->addInputFilter();
}
addElements just adds a Submit and CSRF input.
And here the Controller action:
public function addAction(){
$form = new BusinessForm('create', $this->entityManager);
if ($this->getRequest()->isPost()) {
$data = $this->params()->fromPost();
$form->setData($data);
if($form->isValid()) {
// save Object
return $this->redirect()->toRoute('subcontractor', ['action'=>'index']);
}
}
return new ViewModel([
'form' => $form
]);
}
The form validates and i can get the Data from the form with $form->getData(). But i cant figure out how to get a populated Object from the form using the form's hydrator. When I use setObject(new Business()) at the start of the controller i get an error while $form->isValid() is running :
Zend\Hydrator\ArraySerializable::extract expects the provided object to implement getArrayCopy()
Isnt that the wrong hydrator being called ?
If i dont setObject() but instead use $form->bind(new Business()) after the validation i get an empty Object from $form->getObject(). If i get the data and hydrate a new Object with the form's hydrator like so : $form->getHydrator()->hydrate($data['business], new Business()) i do get the populated Object i was expecting. (Business being the name of the base fieldset)
So my question is, how to i get the result of the last call from just using $form->getObject() after the validation?
[EDIT]
The Problem seems to be with the Collections of Fieldsets used as sub-fieldsets in the businessfieldset. If i validate the form without using the collections i do get the expected Business Object from $form->getData()
Here an example how i add the collection (in the business fieldset):
$this->add([
'name' => 'emails',
'type' => 'Zend\Form\Element\Collection',
'attributes' => [
'id' => 'business_emails',
],
'options' => [
'label' => 'Emails',
'count' => 1,
'should_create_template' => true,
'template_placeholder' => '__index__',
'allow_add' => true,
'target_element' => [
'type' => 'LwsSubcontractor\Form\EmailFieldset',
],
'target_class' => 'LwsSubcontractor\Entity\Email'
],
]);
and here the EmailFieldset:
public function __construct() {
parent::__construct('email');
$this->setObject(new Email());
$this->addElements();
}
protected function addElements() {
$this->add([
'name' => 'email',
'type' => 'Zend\Form\Element\Email',
'attributes' => [
'placeholder' => 'E-Mail (z.B. email#muster-email.de)',
'class' => 'form-control',
'required' => true,
'size' => 50,
],
'options' => [
'label' => 'Email',
],
]);
}
}
If using the Collections i get the Error message from above. So after adding a hydrator to each Fieldset i was fine.
Although i was under the impression that setting the hydrator for the form would result in the used fieldsets to inherit that hydrator.Or was this because of using the fieldsets as collections and not directly ?
You have to add the hydrator to all your fieldsets, personally I use DoctrineModule\Stdlib\Hydrator\DoctrineObject for doctrine entities.
I would also look at using the init() method to initialize your forms and add elements then register and retrieve your form and fieldsets through the FormElementManager, $serviceLocator->get('FormElementManager')->get(yourFieldsetorForm::class). The form can than be injected into your controller.
I hope this helps.

Resources