Using select2_from_ajax with a JSON datasource - php

I am new to Laravel and to Backpack for Laravel so please bear with me. I am trying to create a client registration form that features a "State" field which is populated dynamically depending on the value selected for the "Country" field.
I am following the instructions provided by Backpack's author here: https://backpackforlaravel.com/docs/3.5/crud-fields#select2_from_ajax
Both states and countries come from this dataset: https://github.com/antonioribeiro/countries. They are returned as collections but they are not read from the DB.
Table schema for Clients (simplified)
+-------------------+---------+--------+
| Field | Type | Length |
+-------------------+---------+--------+
| uuid | CHAR | 36 |
| name | VARCHAR | 128 |
| city | VARCHAR | 128 |
| state | VARCHAR | 64 |
| country_iso_cca2 | VARCHAR | 2 |
+-------------------|---------+--------+
The good part
The "Country" field works just fine. It fetches data from the JSON dataset at creation and reads/writes info from or to the DB on update/save:
// ClientCrudController.php
$countries = new Countries();
$allCountriesCodes = $countries->all()->pluck('name.common', 'cca2')->toArray();
$this->crud->addField([
'name' => 'country_iso_cca2',
'label' => 'Country',
'type' => 'select2_from_array',
'options' => $allCountriesCodes,
'allows_null' => false,
'default' => 'US',
]);
The bad (incomplete) part
// ClientCrudController.php
$this->crud->addField([
'name' => 'state',
'label' => 'State',
'type' => 'select2_from_ajax',
'entity' => 'foobar', // the method that defines the relationship in your Model
'attribute' => 'name',
'data_source' => url('api/states'),
'placeholder' => 'Select a country first...',
'dependencies' => ['country_iso_cca2'],
]);
Calling /admin/client/create will result in an error ("Call to undefined method App\Models\Client::foobar").
I understand that the error is raised because there is no model defined for States and hence no relationship. My problem is, I do not understand what the implementation is supposed to look like in a case like this where the two select fields do not represent separate entities at an ORM level.
Is it possible to implement this kind of dependency in a "backpack-native" way, without resorting to creating a custom field type?

Related

Is there some option to create Yii2 ActiveDataProvider from query and static data?

I use ActiveDataProvider to display some data using GridView. Also I have some data retrieved from API and this is a array. For example:
$emails = [
'123' => 'john#test.io',
'234' => 'jane#test.io',
'345' => 'jake#test.io'
];
$query = new Query();
$query->select(['id', 'username'])->from('user');
$provider = new \yii\data\ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 100,
],
]);
Result of displaying this ActiveDataProvider will be something like this:
+------+--------------+
| id | username |
+------+--------------+
| 123 | John |
| 234 | Jane |
| 345 | Jake |
+------+--------------+
How can I join data from query and array to get something like this:
+------+--------------+-------------------+
| id | username | email |
+------+--------------+-------------------+
| 123 | John | john#test.io |
| 234 | Jane | jane#test.io |
| 345 | Jake | jake#test.io |
+------+--------------+-------------------+
I found the only one option - create an array from query and merge it with existing array, but it looks bad for me. Is there any other solution?
You are probably looking for this
https://www.yiiframework.com/doc/api/2.0/yii-data-arraydataprovider
An ArrayDataProvider acts in a lot of the ways the same as an ActiveDataProvider and you can use it as the base data for most of the Yii2 controls. I would get the ActiveDataProvider data, parse it, match it to what you already have by default and create an array that can be used as the base of the ArrayDataProvider.
If you are putting it into a DataGrid you can always do something like this for the email column
[
'class' => 'yii\grid\DataColumn',
'attribute' => 'email',
'format'=>'raw',
'value'=>function ($model) use ($emails) {
return $emails[$model->id];
},
],
A way , for complex or aggregated model, could be based on adding and afterFind method in your model
add the property you need and populate you value with a proper function eg:named getYourRelatedEmail()
or you could use an assigment depending the way you obatin the email array or the related values)
In your model
public $userEmail;
public function afterFind()
{
$this->userEmail = getYourRelatedEmail();
}
in this way you can acces in gridview to the model data field as common model field
In this way you could retrive all the data you need form different sources and build yuor composite model as you need ..

Laravel updateOrCreate - not updating correctly

I have 3 tables in my DB files, file_types and post_files and when I am saving file type first to my DB like this:
private function findOrCreateFileType($fileType)
{
return $fileType = FileType::firstOrCreate([
'name' => $fileType,
'slug' => str_slug($fileType, '-')
]);
}
Then after that I am saving a file to DB and updating 'content_files' table as well like this:
$file = File::updateOrCreate([
'cms_id' => $inventoryRemoteId,
'file_name' => $file->post_name,
'file_path' => $file->guid,
], [
'file_type_id' => $fileType->id,
'file_extension' => pathinfo($file->guid)['extension'],
'file_size' => $fileInfo['Content-Length']
]);
PostFile::updateOrCreate([
'file_id' => $file->id,
'post_id' => $contentId,
],[
'file_type_id' => $fileType->id,
]);
The problem that I have is when I am saving a post that has for example 2 files of 2 different types, for example an attachment and a feature image then it is correctly saving in to the file_types table:
ID | name
--------------------
1 | feature_image
--------------------
2 | attachment
But, then when I am saving to files table, it is not saving correctly since it is giving the last type to every file for that post:
ID | file_type_id | name
--------------------
1 | 1 | feature_image file name
--------------------
2 | 1 | attachment file name
So, since I am first saving feature-image and then attachments it is for some reason updating the file_type_id of the feature-image in the files table, as well as in the post_files table. What am I doing wrong, how can I fix this?

No searching on primary key with Laravel Scout?

I'm using Laravel Scout with TNTSearch Engine at it's working fine but with one little problem. I have the following records.
| ID | Name |
+---------+----------+
| 9030100 | Car |
| 9030150 | Car2 |
| 9030200 | Radio |
Here is my query:
CatalogProducts::search( $query )->paginate( 15 );
When I'm looking for 'car,' it's returning all records with 'car' in the name.
When I'm looking for '9030100', it's returning the product 'Car.'
But when I'm looking for '9030', I don't have any results. Why? How do I fix it?
try changing the fuzziness.
set fuzziness to true.
'tntsearch' => [
'storage' => storage_path(), //place where the index files will be stored
'fuzziness' => true,
'fuzzy' => [
'prefix_length' => 2,
'max_expansions' => 50,
'distance' => 2
],
'asYouType' => false,

CakePHP empty Fields of associations are saved instead of discard

I have a small project on CakePHP. It has a table named articles, and 5 tables of Fields like categories, tags, images, etc. The associations are mostly HasOne, and associated tables has multiple column.
When saving data on articles table everything look good, but on some associations, for example: Article -> Rating, if I did not fill up some fields of Rating tables, that are being saved as null:
+----+------------+--------------+
| id | article_id | rating_value |
+----+------------+--------------+
| 1 | 36 | 3 |
| 2 | 56 | 5454.56 |
| 3 | 57 | 4 |
| 5 | 51 | NULL |
+----+------------+--------------+
If I add some validations, then I can't save the article as it need to be validated. All I want is that if rating_value is empty, then it must not be created as null (entity rejected), and the article must be saved.
Deleting articles works as expected, all related entities are deleted.
I tried altering $data on Model.beforeMarshall but the class is private in both Tables Articles and Ratings (i think associations may be the problem).
Some code (controller add):
public function add()
{
$article = $this->Articles->newEntity();
if ($this->request->is('post')) {
$article = $this->Articles->patchEntity($article, $this->request->data, [
'associated' => [
'Ratings',
]
]);
if ($this->Articles->save($article)) {
$this->Flash->success(__('Saved.'));
return $this->redirect(['action' => 'index']);
}
}
$this->set('article', $article);
}
I deleted all validations of every associated Model because of this.
// Articles Table
$this->hasOne('Ratings', [
'className' => 'Ratings',
'dependent' => true,
]);
// Ratings Table
$this->belongsTo('Articles', [
'foreignKey' => 'article_id',
'joinType' => 'INNER'
]);
// Rating.php Entity
protected $_accessible = [
'*' => true,
'id' => false
];
// Article.php Entity
protected $_accessible = [
'*' => true,
];
If you are setting rating_value then it will try to save and validate with all validation rules.
You can remove the associated data if rating_value is empty like this
$dataToSave = $this->request->data;
if(empty($dataToSave['rating']['rating_value'])){
unset($dataToSave['rating']);
}
$article = $this->Articles->patchEntity($article, $dataToSave, [
'associated' => [
'Ratings',
]
]);
I thing every thing looks good.May be your field in table has default value as null. For example:
If 'rating_value' field in your table has default value null,
make that default full to none first.
If validation fails that don't give you save anything rather than saving null value.If that still don't work for you see:
debug($this->request->data);
And look there if data are coming correctly from your view(form).
Another important thing is that you can use different validation set for association. (see here)

How should I link these models to query multi tables via CakePHP associations?

I am using cakephp 2.3.2 and I need to do a query on multi tables.
I have this database:
--------------------------------------------------------------
| Users | Agents | Companies | Ads |
--------------------------------------------------------------
| id | id | id | id |
| username | name | company | title |
| password | lastname | sector | message |
| | user_id | user_id | user_id |
--------------------------------------------------------------
These are the associations (Models):
User
hasOne Agent
hasOne Company
hasMany Ads
Agent
belongsTo User
Company
belongsTo User
Ad
belongsTo User
(NOTE: Please, keep in mind that when I add a new user that user could be an Agent OR a Company.)
QUESTION:
In my AdsController I have an action named view, there I read two params that I receve from Route:
$this->params['id']
$this->params['sector']
I need to do a query to check if the id is really associated to an Ad.id and If the sector is associated to Company.sector
I would like to check it with ONE find('first') instead of checking
If the ID exists
If the sector exists and it is associated to the user_id
How could I do it ?
(If the query finds Ad.id and Company.sector I need to retrieve all fields of Ad and Company)
At the moment my find('first') in AdsController/view is:
$options = array(
'fields' => array(
'Ad.*'
),
'contain' => array(
'User' => array(
'Company' => array(
'fields' => array(
'Company.*'
),
'conditions' => array(
'Company.sector' => $this->params['sector'],
),
)
)
),
'conditions' => array(
'Ad.id' => $this->params['id'],
)
)
$data = $this->Ad->find('first', $options);
debug($data);
The problem is that Company is now shown in the result (array).
Please, keep in mind that I only need to retrieve the array IF:
The ID of the AD exists
The sector of the Company exists
If one of above are not "true" I would like to get an empty array.
Obviously I have added Containable behavior in Ad model.
I've seen this, had same problem that for some reason contain, kinda malfunctions with that array() structure, maybe it works how it means to be, but I dont get it, anyways to retrive chain dependencies i use this kind of construction:
$options = array(
'contain' => array(
'User',
'User.Company' => array(
'conditions' => array(
'Company.sector' => $this->params['sector'],
),
)
),
'conditions' => array(
'Ad.id' => $this->params['id'],
)
)
But, you need to have in mind that conditions in contain are not the same as the conditions for main model, therfore even if the company sector does not exist but Ad with given id exits you will not receive an empty set. As far as I know you cannot use a condition to exclude results if there is no direct association between models. So you would still need to check if $result['User']['Company'] is an empty set or not.
One more thing your query array does not match structure you've provided with your question, there is no field sector in your Company table

Categories