Laravel backpack addField where clause - laravel

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).

Related

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

ListEntries in table for relationship on show page - backpack for laravel

Just new with backpack. I search on official site and googled it, but dit not found an answer
In laravel 7, using Backpack 4.1
My data model is : Customer has many addresses
Relationship is configured in the Customer model :
public function addresses()
{
return $this->hasMany(\App\Models\Address::class, 'user_id');
}
Relationship is configured in the Address model :
public function customer()
{
return $this->belongsTo(\App\Models\Customer::class);
}
public function country()
{
return $this->belongsTo(\App\Models\Country::class);
}
public function address_type()
{
return $this->belongsTo(\App\Models\AddressType::class);
}
In my customer show page, I would like to show all customer addresses in a table, just under the customer details.
So in my CustomerCrudController, I have implemented this method :
protected function setupShowOperation()
{
$this->crud->set('show.setFromDb', false);
$this->crud->addColumn(['name' => 'name', 'type' => 'text', 'label' => __('models/customers.fields.name'), ]);
$this->crud->addColumn(['name' => 'email', 'type' => 'email', 'label' => __('models/customers.fields.email'), ]);
$this->crud->addColumn([
'name' => 'addresses',
'label' => __('models/addresses.plural'),
'type' => 'table',
'columns' => [
'address_type_id' => __('models/addresses.fields.address_type'),
'address_type.name' => __('models/addresses.fields.address_type'),
'address1' => __('models/addresses.fields.address1'),
'address2' => __('models/addresses.fields.address2'),
'city' => __('models/addresses.fields.address2'),
'postal_code' => __('models/addresses.fields.address2'),
'country.name' => __('models/countries.singular'),
],
]);
}
When I go on my page : /admin/customer/3/show,
In my debugbar, I saw the query how load addresses
select * from `addresses` where `addresses`.`user_id` = 3 and `addresses`.`user_id` is not null
I have the table rendered with the corresponding number of lines from data in DB, but rows are blank.
Is this the correct way to do that ? What are the correct parameters ?
Is there a way to show a table with action buttons (show entry, edit) - same as in List view ?
Should it be implemented in another way ?
Hope I'm clear.
Thanks
Don't know if it is a laravel bug, but my solution was to create my own table blade, base on the file :
\vendor\backpack\crud\src\resources\views\crud\columns\table.blade.php
and have created my own :
\resources\views\vendor\backpack\crud\columns\address_table.blade.php
I have juste changed the file:40
#elseif( is_object($tableRow) && property_exists($tableRow, $tableColumnKey) )
to
#elseif( is_object($tableRow) && isset($tableRow->{$tableColumnKey}) )
now, in my CustomerCrudController.php :
protected function setupShowOperation()
{
$this->crud->set('show.setFromDb', false);
$this->crud->addColumn(['name' => 'name', 'type' => 'text', 'label' => __('models/customers.fields.name'),]);
$this->crud->addColumn(['name' => 'email', 'type' => 'email', 'label' => __('models/customers.fields.email'),]);
$this->crud->addColumn([
'name' => 'addresses',
'label' => __('models/addresses.plural'),
'type' => 'address_table', // my custom type
'model' => \App\Models\Address::class,
'entity' => 'addresses',
'columns' => [
'address_type_name' => __('models/addresses.fields.address_type'),
'postal_code' => __('models/addresses.fields.postal_code'),
'city' => __('models/addresses.fields.city'),
'address1' => __('models/addresses.fields.address1'),
'address2' => __('models/addresses.fields.address1'),
],
]);
}
And I've added an accessor in my model (Address.php)
public function getAddressTypeNameAttribute()
{
return "{$this->address_type->name}";
}
Don't know if there is a better way ...
Hope this will help others.
I use Laravel 8,
In addition for the answer above, and based on this answer https://stackoverflow.com/a/65072393 and https://stackoverflow.com/a/43011286/1315632 regarding PHP function property_exists vs Laravel magic methods to create dynamic properties and methods
After creating the overwrite column php artisan backpack:publish crud/columns/table
I change line 40 in file:\resources\views\vendor\backpack\crud\columns\table.blade.php into
#elseif( is_object($tableRow) && ( property_exists($tableRow, $tableColumnKey) || property_exists((object)$tableRow->toArray(), $tableColumnKey) ) )
adding OR checking from answer https://stackoverflow.com/a/65072393

Backpack Laravel Admin Incorrectly Redirects after Editing Model

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',
],
],

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.

correct value instead of array

scenario is crm with tables account, account_contact, contact and account_contact_role. The latter contains roles like 'project lead' or 'account manager' for the combos defined in the junction table.
My challenge is the account view, that is listing also the connected persons with their roles. I want my grid to show: Doe | John | employee.
The problem is now when the contact has 2+ entries in the junction table. How can I print the correct role for the row? As you can see in the code I solved it the static way which shows only 1 out of n times the correct value. Tried it with inner join without success. Is it a problem of the search in the model or with the access in the view?
the relation from the account model to the contacts:
public function getContacts($role = null)
{
// many-to-many
return $this->hasMany(ContactRecord::className(), ['id' => 'contact_id'])
->via('accountContacts')
->innerJoinWith(['accountContacts.role'])
->andWhere(['account_contact.account_id' => $this->id])
->andWhere(['account_contact_role.type' => $role])
;
}
the view
<?= \yii\grid\GridView::widget([
'dataProvider' => new \yii\data\ActiveDataProvider([
'query' => $model->getContacts('internal'),
'pagination' => false
]),
'columns' => [
'lastname',
'firstname',
[
'label' => 'Role',
'attribute' => 'accountContacts.0.role.name',
],
[
'class' => \yii\grid\ActionColumn::className(),
'controller' => 'contacts',
'header' => Html::a('<i class="glyphicon glyphicon-plus"></i> Add New', ['contact-records/create', 'account_id' => $model->id]),
'template' => '{update}{delete}',
]
]
]); ?>
defined relations are:
account has many accountContacts has one contact
accountContacts has one accountContactRole
Many thanks in advance!
You are showing account's contacts, so you have to list from Contact model.
Inside Contact model (or Contact ActiveQuery file):
public static function queryContactsFromAccountAndRole($account, $role = null)
{
// many-to-many
return ContactRecord::find()->innerJoinWith(['accountContacts' => function($q) use($account, $role) {
$q->innerJoinWith(['accountContactsRole'])
->andWhere(['account_contact.account_id' => $account->id])
->andWhere(['account_contact_role.type' => $role]);
}])
->andWhere(['account_contact.account_id' => $account->id]);
}
Now you have one record for each contact and the gridview will show all contacts.

Resources