Save custom data into pivot table using select2_multiselect - php

I currently have a users table and a books table, with a pivot table user_book which has user_id, book_id as well as book_tag (this can be 'H' for happy, 'S' for sad or 'A' for angry)
Against the advice of the backpack team, we are looking to have three multiselect options, which will popoulate with the 3 different types of book tags, i.e. Happy books, Sad books, and Angry books.
I currently have the following definition inside the initFields function:
<?php
namespace App\Http\Controllers\Admin;
class UserCrudController extends UserCrudController
{
// ....
protected function initFields()
{
// crud fields here
$this->crud->addField([
'label' => "Happy books",
'type' => 'select2_multiple',
'name' => 'books_h',
'entity' => 'books',
'model' => "App\Models\Book",
'pivot' => true,
]);
}
}
This however, does not seem to save. Any assistance is greatly appreciated

you need to make sure the the relation books_h is defined correctly in your entity as belongsToMany relationship based on laravel docs https://laravel.com/docs/9.x/eloquent-relationships#many-to-many
like so
public function books_h():
\Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(Book::class, 'user_book', 'user_id', 'book_id', 'id', 'id')->withPivot('book_tag');
}
then you need to overwrite both create and update methods to update the request for these fields to transfer it to look like so
[
1 => ['book_tag' => 'H'],
2 => ['book_tag' => 'H'],
]
before calling $response = $this->traitUpdate();
refer to this link for more info https://backpackforlaravel.com/docs/5.x/crud-operation-update#override-the-update-method
but I would recommend to use the correct way backpack team mentions https://backpackforlaravel.com/docs/5.x/crud-fields#save-additional-data-to-pivot-table even if you want to have it as 3 separate fields you can define the subfields as hidden with the value you want

Related

Make a select2_multiple sortable

How do we make a select2_multiple sortable? I have a users table and a badges table. Naturally, I want to have a user_badge pivot table which will keep track of all the badges a user has and also sort it based on the order that I define using the select2_multiple.
Currently, I have my setupCreateOperation() setup as follows:
protected function setupCreateOperation()
{
$this->crud->addField([
'name' => 'badges_multi',
'label' => 'Badges',
'type' => 'select2_multiple',
'attribute' => 'internal_name',
'entity' => 'badges_multi',
'model' => 'App\Models\Badges',
'pivot' => true,
'sortable' => true,
'sortable_options' => [
'handle' => '.my-custom-handle',
],
]);
}
This returns the select2_multiple but the input is not sortable, i.e. I can drag and drop to rearrange. It only returns badges in alphabetical order
The select_multiple field does not have the ordering functionality.
To order you can use the select_and_order field or the repeatable field
You can also create a custom version of the select_multiple field that has the order functionality if you want to implement it that way.
Doing php artisan backpack:field select_multiple_with_order --from=select_multiple would create you a new file in your resources folder that is equal to Backpack select_multiple, from there you can add the functionality you need, and re-use it in other cruds if you need it there too.
Wish you the best.

how to insert the pivot relationship in laravel

consider i have 2 models that have the pivot relationship many to many between them . now when i want to insert the pivot table how can i achieve it currently i am doing this :
DB::table('model1_model2')
->insert([
'something' => $something,
'something2' => $something2,
]);
and i kinda feel that its not right and i have do save it with some relation ship or sync !! any idea how to do this ?
EDIT‌:
Added relationship
public function accommodationRoom()
{
return $this->belongsToMany(AccommodationRoom::class)->withPivot('guest_first_name','guest_last_name','guest_cell_phone','guest_nationality_id');
}
As mentioned in the Inserting & Updating Related Models - Many To Many Relationships documentation:
When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table:
$model1->accommodationRoom()->attach($accommodationRoomId, [
'something' => $something,
'something2' => $something2,
]);
If you're attaching multiple relations then you would would pass a multidimensional array with the keys are the id of the relation and the values are the arrays of additional data:
$model1->accommodationRoom()->attach( [
1 => ['something' => $something, 'something2' => $something2,],
3 => ['something' => 'something else', 'something2' => $something2,],
]);
The same is true for the sync() method as well.

Junction table in yii php

I would like to create a junction table tbl_guid_cost_centre that gets taken care of without me manually saving it to the database. I tried adding this to my relations:
'costCentre' => [
self::HAS_ONE,
'CostCentre',
'guid_to',
'foreignKey' => 'guid',
'tbl_guid_cost_centre(guid_to, cost_center_id)',
"order" => "id desc"],
so that my when saving the costCentre, a row is created for it in my tbl_guid_cost_centre. However I'm getting the error:
Property "CHasOneRelation.0" is not defined.
Any suggestion?
You can have your junction table with the keyword through in your relations:
public function relations() {
'guidCostCentre' => [
self::HAS_ONE,
'GuidCostCentre',
['guid_to' => 'guid']
],
'costCentre' => [
self::HAS_ONE,
'CostCentre',
'cost_centre_id',
'through' => 'guidCostCentre'
]
}
You're defining HAS_ONE relation in a wrong way. The first three elements of relation configuration array should be: relation type, related model name and foreign keys definition. All further elements should be indexed by keys related to relation properties. 'tbl_guid_cost_centre(guid_to, cost_center_id)', probably generates this error, because it does not have a key, so it is treaded as a value for 0 property. You didn't share any details, so it is hard to guess what you want to achieve, but you should start from something like this:
'costCentre' => [
self::HAS_ONE,
'CostCentre',
'guid_to',
'order' => 'id desc',
],
And add additional settings at the end array with correct key.
Some examples and explanation you can find in the documentation: https://www.yiiframework.com/doc/guide/1.1/en/database.arr#declaring-relationship

CakePHP: Retreiving a column from the join table in a belongsToManay relationship

My application allows a user to create scenarios by linking together soe_blocks. In turn, soe_blocks refer to a variable number of soe_entries.
To build scenarios, soe_blocks are linked to the scenario and ordered by an offset. The soe_blocks can be used in many different scenarios. soe_entries can relate only to a single soe_block
I think the relationship is defined as:
scenarios belongsToMany soe_blocks through scenarios_soe_blocks
soe_blocks belongsToMany scenarios through scenarios_soe_blocks
scenarios_soe_blocks is where the offset is kept
soe_entries haveOne soe_blocks
Tables:
scenarios: id | name
data: 0, 'scenario_1'
soe_blocks: id | name
data: 0, 'soe_block_1'
1, 'soe_block_2'
scenarios_soe_blocks: id | scenario_id | soe_block_id | offset
data: 1, 0, 1, 1
2, 0, 2, 2
Models:
class ScenariosTable extends Table
{
$this->belongsToMany('SoeBlocks', [
'foreignKey' => 'scenario_id',
'targetForeignKey' => 'soe_block_id',
'through' => 'ScenariosSoeBlocks',
'joinTable' => 'soe_blocks'
]);
}
class SoeBlocksTable extends Table
{
$this->belongsToMany('Scenarios', [
'foreignKey' => 'soe_block_id',
'targetForeignKey' => 'scenario_id',
'joinTable' => 'scenarios_soe_blocks',
'through' => 'ScenariosSoeBlocks'
]);
}
class ScenariosSoeBlocksTable extends Table
$this->belongsTo('SoeBlocks', [
'foreignKey' => 'soe_block_id',
'joinType' => 'INNER'
]);
}
Controllers:
public function view($id = null)
{
$scenario = $this->Scenarios->get($id, [
'contain' => ['SoeBlocks', 'RunStatus', 'ScenarioLog']
]);
$this->set('scenario', $scenario);
}
As far as I can make out from CakePHP Doc, this is all I need. But I couldn't get the ScenarioController->view() method to return the offsets from the scenarios_soe_blocks table associated with the soe_blocks.
I tried to add ScenariosSoeBlocks into the 'contain' clause in the ScenarioController, but got the error: Scenarios is not associated with ScenariosSoeBlocks. I found an SO article that suggested I add the following to the ScenarioTable:
$this->hasMany('ScenariosSoeBlocks', [
'foreignKey' => 'scenario_id'
]);
This seems to have worked, and now I can request ScenariosSoeBlocks in my controller like this:
$scenario = $this->Scenarios->get($id, [
'contain' => ['SoeBlocks', 'ScenariosSoeBlocks', 'RunStatus', 'ScenarioLog']
]);
Which at least gets the data into the view template, but not in the single object I'm hoping for. Eventually, I want to be able to CRUD the soe_blocks along with their associated soe_entries, in an object that looks like this:
offset | soe_block_id | soe_entry_id |
I have many other questions, like how to save etc., but I figured I need to get this working first.
So, my questions for now are:
are my associations correct?
how do I retrieve all the associations to view?
are my associations correct?
The first two are, but then it should be:
soe_blocks hasOne soe_entries
soe_entries belongsTo soe_blocks
how do I retrieve all the associations to view?
By containing them, just like you did in your first example. This question seems to originate from the question how to access the join table data, which is very simple, the join table data is being set on the target table entity (Scenario or SoeBlock, depending on from which side/table you issue the query), in a property named _joinData:
$joinTableEntity = $scenario->soe_blocks[0]->_joinData;
$offset = $joinTableEntity->offset;
You can easily gather information about the data structure by dumping your entity contents:
debug($scenario);
See also
Cookbook > Database Access & ORM > Associations - Linking Tables Together
Cookbook > Database Access & ORM > Saving Data > Saving Additional Data to the Join Table

association count within selection

I have a Candidate doctrine entity, this entity has a many to many association to a entity Group
On the index page i show all candidates.
Now i made a form with a ObjectMultiCheckbox object for the Group entity.
After selecting a checkbox and submitting the form i use the doctrine QueryBuilder to only show the candidates that have a association with the selected entity.
This works fine but in the form i'd like to show how many candidates are associated with a certain group.
I can do this using the label_generator option in ObjectMultiSelectCheckbox definition in the fieldset:
namespace MeCandidate\Form;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class SearchFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct($objectManager)
{
parent::__construct($name = 'search');
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectMultiCheckbox',
'name' => 'function',
'attributes' => array(
'class' => 'searchCriteria'
),
'options' => array(
'label' => 'Group',
'object_manager' => $objectManager,
'target_class' => 'MeCandidate\Entity\Group',
'label_generator' => function($targetEntity) {
return ' ' . $targetEntity->getName() . '('. $targetEntity->getCandidates()->count() .')';
},
),
)
);
}
public function getInputFilterSpecification()
{
return array(
array(
'name' => 'group',
'required' => false,
),
);
}
}
This all works as expected.
So when i select Group X it shows all candidates that have a association with Group X.
But now that only a selection of all candidates is shown the number of candidates associated with a certain group is no longer accurate as this shows the total number of candidates associated with the group but i need the number of candidates associated with the group IN the current selection.
I think i can fix it by injecting the current QueryBuilder into the form when creating the form. However i can't find a good way to get the right count.
I hope what i'm trying to do is clear, if not ask me anything.
UPDATE:
I have a working solution, in my Group entity i have a method getCandidateCount it takes a ArrayCollection of the current selection of candidates and checks if a associated candidate is in the selection. I adapted the label_generator function in the fieldset to call getCandidateCount
The problem with this is that after validating the form i have to recreate the form and inject the current selection of candidates into it so i can pass it to the getCandidateCount method inside the label_generator function.
It works but not very elegant so if anybody has ideas about how to it better i would love to hear them!
Thanks,
Yoram

Categories