OCTOBERCMS Dropdown options dependent on selected value on other dropdown - php

I'm stuck with this problem and i can't figure out how to solve after some time searching for a example.
The two dropdowns options are table dependent on their values.
I have the one table with 'area' values (nested with simple tree working ok) with the following structure on fields.yaml file:
fields:
id:
label: Número
oc.commentPosition: ''
span: auto
disabled: 1
type: number
area_id:
label: 'Parente de'
oc.commentPosition: ''
emptyOption: 'Sem valor'
span: auto
type: dropdown
area:
label: Área
oc.commentPosition: ''
span: full
required: 1
type: text
I also have another table 'modulos' values with the following structure in fields.yaml:
fields:
modulo:
label: Módulo
oc.commentPosition: ''
span: auto
required: 1
type: text
area:
label: Área
oc.commentPosition: ''
nameFrom: area
emptyOption: 'Sem valor'
span: auto
descriptionFrom: id
type: relation
In the 'Area' model I have:
...
public $hasMany = [
'modulos' => ['JML\Gkb\Models\Modulos']
];
In the 'Modulos' model I have
....
public $belongsTo = [
'area' => ['\JML\Gkb\Models\Area']
];
I have other model that have relations with previous fields and two dropdown fields working ok without any filter, and the troubleshoting field (modulos) where I can't find a way to filter based on the values of 'Area' dropdown I have the following in fields.yaml.
....
modulo_id:
label: mod
oc.commentPosition: ''
emptyOption: 'Sem valor'
span: auto
required: 1
dependsOn:
area
type: dropdown
tab: Geral
In my models PHP file where I have the dropdowns defined, I have:
public function getModuloIdOptions() {
return Modulos::where('area_id', '=', $this->area)->lists('modulo', 'id');
}
That to me seems logical (maybe not) and I tried with DB also and many more other. I tried with dd() to see if I can get the values from the first dropdown to no avail. If I try to filter the values, no value appears at all (except an empty value).
Any help out there ???
TIA
JL

The dataset is passed as the second argument to get the "getOptions" method. Here is an alternative approach that may work:
public function getModuloIdOptions($value, $data) {
return Modulos::where('area_id', '=', array_get($data, 'area'))->lists('modulo', 'id');
}
You may also want to try accessing the area_id value:
public function getModuloIdOptions(){
return Modulos::where('area_id', '=', $this->area_id)->lists('modulo', 'id');
}
Or less efficiently the area->id value (may require exception handling):
public function getModuloIdOptions(){
return Modulos::where('area_id', '=', $this->area->id)->lists('modulo', 'id');
}

I solved the problem with that dropdown and others with the same objective with the following steps:
I have the the 'Relation' widget on them. Changed them to 'Dropdown' widget.
Defined the 'Depends on' field.
Defined the 'Preset' field to the one above. I think that is the missing link to the problem solution not documented anywhere and i get there on try/error basis (may be it is valuable to add this to the October documentation).
Filter options with the snipet code on the end of my question or the second snipet of Samuel answer.
That solved my problem.
Thanks all.

Related

Replace "label.form.empty_value" of expanded entity type

I want to render a relation/association field (i.e. EntityType) in EasyAdmin as radio buttons. My setup:
- property: 'group'
type_options:
expanded: true
What I'm getting is:
o label.form.empty_value
o Admin
o User
How can I replace label.form.empty_value to something like "None"?
Just add a placeholder like this:
- property: 'group'
type_options:
expanded: true
placeholder: 'None'

Seed multiple tables from one backend/builder form?

I'm trying to insert data into multiple related tables from one (builder) back end form/model - but it seems I'm doing something wrong.
t1 posts (used for model Posts.php)
id, flag1, flag2 , created_at, updated_at
t2 post_content (used for model Posts_content.php)
id, content
I've tried expanding the model (Posts.php) used for the form as explained in the relations documentation of octobercms like so:
public $hasOne = ['content' => ['Test\Gcn\Models\Post_content', 'key' => 'id', 'otherKey' => 'id']];
While this does not produce an error when a record is created via the backend controller, no data is actually written to posts_content.
I've also tried to solve it with a proxy field
fields.yaml (of model Posts.php)
post_content[content]:
label: 'Post here'
size: ''
mode: tab
span: full
type: markdown
Posts.php (with the proxy field)
public function formExtendModel($model)
{
/*
* Init proxy field model if we are creating the model
*/
if ($this->action == 'create') {
$model->post_content = new Post_content;
}
return $model;
}
According to the error message, the array needs to be set to be jsonable. But even after that it looks like it's trying to insert the data in the wrong table.
What is the proper way to achieve this?
I'm trying to have one form where a user can enter some flags (checkboxes and the like) and a text field which should be inserted into post_content table with the correct id.
I appreciate your time and help, thank you!
From my opinion, you have to do many changes in your structure like below:
1) Add content_id in your posts table
id, flag1, flag2, content_id, created_at, updated_at
And second table should be contents table
id, content
content_id will be used to create one to one relationship between Post and Content. For more information about relations click here.
2) Then, you have to define models Post for posts and Content for all contents.
In Content model give One to one relationship.
public $hasOne = [
'post' => 'Test\Gcn\Models\Post'
];
If you give this type of relation, this will find content_id in your Post model so it set direct relationship between Post and Content.
3) Your form field should be like
In Post model fields.yaml should be
fields:
flag1:
label: 'Flag 1'
oc.commentPosition: ''
span: left
type: text
flag2:
label: 'Flag 2'
oc.commentPosition: ''
span: auto
type: text
And In Content model fields.yaml should be
fields:
post:
label: Post
oc.commentPosition: ''
nameFrom: flag1
descriptionFrom: description
span: auto
type: relation
content:
label: Content
size: small
oc.commentPosition: ''
span: left
type: textarea
and columns.yaml should be
columns:
content:
label: Content
type: text
searchable: true
sortable: true
post:
label: Post
select: flag1
relation: post
searchable: true
sortable: true
For more details about the backend, you can go here...1) form relation 2) column relation.
This all is from my side. Now analyze all this and try your code. And tell me if you have any query.
Thank you.

OctoberCMS - Accessing model data in back-end... Returning null?

The problem that I'm having is, I cannot access certain information from the File Model such as File Path, File Name, and other information stored in the system_files table. I believe I have pinpointed where the issue is coming from, but I'm not sure how to fix it. Below I'll show some of the code.
1) File.php ~ File Model
<?php namespace Compuflex\Downloads\Models;
use Model;
use October\Rain\Database\Attach\File as FileBase;
/**
* Model
*/
class File extends FileBase
{
use \October\Rain\Database\Traits\Validation;
/*
* Validation
*/
public $rules = [
];
/*
* Disable timestamps by default.
* Remove this line if timestamps are defined in the database table.
*/
public $timestamps = false;
/**
* #var string The database table used by the model.
*/
public $table = 'compuflex_downloads_files';
public $attachOne = [
'file' => ['System\Models\File',
'key' => 'id']
];
public $belongsToMany = [
'user' => ['RainLab\User\Models\User']
];
}
In this file, you can see there are two relationships identified: $attachOne which attaches the file upload to the File Model as well as $belongsToMany which identifies the many-to-many relationship between users and files. (Many files can belong to Many users, or one user can have many files, but one file can also belong to many users). This is the file that sets up the file and user relation which you will see in the columns.yaml file. Just a side note, the only reason I set the 'key' =>'id' was to ensure that it was actually identifying the 'key' as 'id'.
2) Columns and Fields
I don't think the fields.yaml file is necessary, but just in case, you can find it here:
fields.yaml
columns.yaml
columns:
file_name:
label: 'compuflex.downloads::lang.file.name'
type: text
searchable: true
sortable: true
full_name:
label: 'compuflex.downloads::lang.file.username'
type: text
searchable: true
sortable: true
select: name
relation: user
email:
label: 'compuflex.downloads::lang.file.email'
type: text
searchable: true
sortable: true
select: email
relation: user
file:
label: 'compuflex.downloads::lang.file.path'
type: text
searchable: true
sortable: true
select: file_name
relation: file
I'm more concerned with the columns.yaml file because that is what I am trying to fix is the back-end controller that displays the list of file information as well as information about the users they're "attached" to.
Here is a screenshot of what the view looks like:
As you can see, the "File" Tab is not displaying any information about the file, and it is supposed to be displaying the file_name column from the system_files table or at least from the File Model, but it displays nothing.
Now, what I find interesting, if I change the last entry in the columns.yaml file from the original to:
file:
label: 'compuflex.downloads::lang.file.path'
type: text
searchable: true
sortable: true
Then the following is output in the back-end controller:
Therefore, as you can see, the information IS there (allegedly), but I'm just not sure how to access it properly.
So, I have one last test that I did that I will show you, I knew that if the select: attribute in the columns.yaml file is set to a column name that's not in the system_files table it should produce an SQL Error of some sort (such as column name not found). And I was right... So, I set it to 'ile_name' instead of file_name, just for testing purposes.
Here is what the error message was:
SQLSTATE[HY000]: General error: 1 no such column: ile_name (SQL: select "compuflex_downloads_files".*, (select group_concat(name, ', ')
from "users" inner join "file_user" on "users"."id" = "file_user"."user_id" where "file_user"."file_id" =
"compuflex_downloads_files"."id") as "full_name", (select group_concat(email, ', ') from "users" inner join "file_user" on "users"."id" =
"file_user"."user_id" where "file_user"."file_id" = "compuflex_downloads_files"."id") as "email", (select ile_name from "system_files"
where "system_files"."attachment_id" = "compuflex_downloads_files"."id" and "system_files"."attachment_type" = ? and "field" = ?) as
"file" from "compuflex_downloads_files" order by "file_name" desc)
Now, to pinpoint the issue more, I believe the issue might be coming from:
(select ile_name from "system_files" where "system_files"."attachment_id" = "compuflex_downloads_files"."id" and "system_files"."attachment_type" = ? and "field" = ?)
I think that those ? are causing this query to return NULL results from the table in turn causing there to be no output. I'm not 100% on this, it's just one possibility. The problem is, I don't know where OctoberCMS builds these queries or if there is any simple way to fix that without touching the actual code for October (for obvious reasons).
Just a note: I apologize if I've included TOO much information here, but I have tried to cut it down. I just wanted to show you that I DID try to troubleshoot and solve this myself, but to no avail.
You are too close, you just have to change type: text to type: relation, also you have to set nameFrom attribute:
file:
label: 'compuflex.downloads::lang.file.path'
type: relation
searchable: true
sortable: true
nameFrom: file_name
relation: file

"Unable to reverse value for property path" for Symfony2 form ChoiceType field on Select2

Long story short, in Symfony 2.8 I've got Movie entity with actors field, which is ArrayCollection of entity Actor (ManyToMany) and I wanted the field to be ajax-loaded Select2.
When I don't use Ajax, the form is:
->add('actors', EntityType::class, array(
'class' => Actor::class,
'label' => "Actors of the work",
'multiple' => true,
'attr' => array(
'class' => "select2-select",
),
))
And it works.
I tried to put there an empty Select field:
->add('actors', ChoiceType::class, array(
'mapped' => false,
'multiple' => true,
'attr'=>array(
'class' => "select2-ajax",
'data-entity'=>"actor"
)
))
The Select2 Ajax works, everything in DOM looks the same as in previous example, but on form submit I get errors in the profiler: This value is not valid.:
Symfony\Component\Validator\ConstraintViolation
Object(Symfony\Component\Form\Form).children[actors] = [0 => 20, 1 => 21]
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Unable to reverse value for property path "actors": Could not find all matching choices for the given values
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Could not find all matching choices for the given values
The funny part is the data received is the same as they were when it was an EntityType: [0 => 20, 1 => 21]
I marked field as not mapped, I even changed field name to other than Movie entity's field name. I tried adding empty choices, I tried to leave it as EntityType but with custom query_builder, returning empty collection. Now I'm out of ideas.
How should I do it?
EDIT after Raymond's answer:
I added DataTransformer:
use Doctrine\Common\Persistence\ObjectManager;
use CompanyName\Common\CommonBundle\Entity\Actor;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
class ActorToNumberTransformer implements DataTransformerInterface
{
private $manager;
public function __construct(ObjectManager $objectManager)
{
$this->manager = $objectManager;
}
public function transform($actors)
{
if(null === $actors)
return array();
$actorIds = array();
foreach($actors as $actor)
$actorIds[] = $actor->getId();
return $actorIds;
}
public function reverseTransform($actorIds)
{
if($actorIds === null)
return array();
foreach($actorIds as $actorId)
{
$actor = $this->manager->getRepository('CommonBundle:Actor')->find($actorId);
if(null === $actor)
throw new TransformationFailedException(sprintf('An actor with id "%s" does not exist!', $actorId));
$actors[] = $actor;
}
return $actors;
}
}
Added it at the end of the MovieType buildForm():
$builder->get('actors')
->addModelTransformer(new ActorToNumberTransformer($this->manager));
$builder->get('actors')
->addViewTransformer(new ActorToNumberTransformer($this->manager));
And added service:
common.form.type.work:
class: CompanyName\Common\CommonBundle\Form\Type\MovieType
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: form.type }
Nothing changed. On form submit, reverseTransform() gets the proper data, but profiler shows the same error. That's a big mistery for me now...
You'll need to add a DTO (Data Transformer ) to transform the value received from your form and return the appropriate object .
Since you're calling the value from Ajax it doesn't recognized it anymore as a an object but a text value.
Examples :
Symfony2 -Use of DTO
Form with jQuery autocomplete
The correct way isn't Data Transformer but Form Events, look here:
http://symfony.com/doc/current/form/dynamic_form_modification.html#form-events-submitted-data
In the example you have the field sport (an entity, like your Movie) and the field position (another entity, like actors).
The trick is to use ajax in order to reload entirely the form and use
PRE_SET_DATA and POST_SUBMIT.
I'm using Symfony 3.x but I think it's the same with 2.8.x
When you add data transformers and nothing seems to change, it sounds like the data never goes through your data transformers. The transformation probably fails before your new data transformers are called. Try to add a few lines to your code:
$builder->get('actors')->resetViewTransformers();
$builder->get('actors')->resetModelTransformers();
// and then add your own

Symfony : How to use widgets with i18n forms in backend (doctrine)

I can not manage to have both i18n and tinyMCE widgets on internationalised fields.
If i put both, i will have internationalised fields for all my objects' fields, but no tinyMCE for them. I will have as much tinyMCE fields as i declared, but thew will not correspond to any language, they will be at the beginning or at the end.
It worked perfectly before i internationalized the objects
Here is an example of code :
// config/doctrine/schema.yml
MyObject:
actAs:
I18n:
fields: [title, subtitle, intro, text]
columns:
title: {type: string(500)}
subtitle: {type: string(500)}
intro: {type: string(4000)}
text: {type: string(16000)}
// lib/form/doctrine/MyObject.class.php
public function configure()
{
$this->embedI18n(array('en', 'fr', 'es'));
$this->widgetSchema->setLabel('fr', 'Français');
$this->widgetSchema->setLabel('en', 'Anglais');
$this->widgetSchema->setLabel('es', 'Español');
$this->widgetSchema['intro'] = new sfWidgetFormTextareaTinyMCE(
array(
'width'=>600,
'height'=>100,
'config'=>'theme_advanced_disable: "anchor,image,cleanup,help"',
'theme' => sfConfig::get('app_tinymce_theme','simple'),
),
array(
'class' => 'tiny_mce'
)
);
$this->widgetSchema['text'] = new sfWidgetFormTextareaTinyMCE(
array(
'width'=>600,
'height'=>100,
'config'=>'theme_advanced_disable: "anchor,image,cleanup,help"',
'theme' => sfConfig::get('app_tinymce_theme','simple'),
),
array(
'class' => 'tiny_mce'
)
);
$js_path = sfConfig::get('sf_rich_text_js_dir') ? '/'.sfConfig::get('sf_rich_text_js_dir').'/tiny_mce.js' : '/sf/tinymce/js/tiny_mce.js';
sfContext::getInstance()->getResponse()->addJavascript($js_path);
}
So i guess when i use $this->widgetSchema['intro'], the "intro" name does not correspond to all the i18n "intro" fields. I tried both 'en_intro' and 'intro_en', but it doesn't do any magic.
So maybe you could help me ?
So i found how to do this and i thought it might interest somebody :
Instead of
$this->widgetSchema['intro'] = ...
Put
$this->widgetSchema['en']['intro'] = ...
with all the languages.
also you can use :
$this->widgetSchema->moveField('en',sfWidgetFormSchema::BEFORE,'intro');
move the i18n label and field to before intro field.

Categories