Cakephp 3 - Query Fixture in IntegrationTestTrait - integration-testing

I am having issues querying a loaded Fixture in my TestCase for an IntegrationTestTrait. I want to verify that a record already exist inside of a Fixture, then insert a duplicate record and verify that there is still only 1 record in the database.
During my test case initialization, I set the session variable for authentication.
public function setUp() {
parent::setUp();
$this->loadFixtures(
'Students', 'Users');
// Configure Authentication
$this->session([
'Auth' => [
'User' => [
'id' => 21896,
'institution_id' => 1,
'student_id' => null,
'contact_id' => 91,
'email' => 'AuthenticatedEmail#school.edu',
'role' => 'DSP',
'is_admin' => false
]
]
]);
// Load Tables
$this->Students = TableRegistry::getTableLocator()->get('Students');
}
In my Test Case, I check to see if the Database contains a record, then submit a POST request then test to see if the record did not insert.
public function testAddStudentSuccess() {
$data = [
'institution_id' => 1,
'contact_id' => null,
'id_number' => '200XYZ',
'last_name' => 'Trimor',
'first_name' => 'Paul',
'email' => '1_test#email.com'
];
// Test Pre-condition
$query = $this->Students->find('all')->where([
'id_number' => $data['id_number']
]);
$this->assertEquals(1, $query->count());
$this->post('students/add', $data);
// Test Post-condition
$this->assertResponseSuccess();
$query = $this->Students->find('all')->where([
'id_number' => $data['id_number']
]);
$this->assertEquals(1, $query->count());
}
However, when I run the Test Case, I get the following error:
Notice Error: Undefined variable: _SESSION in/var/www/html/samusg/src/Model/Table/StudentsTable.php, line 206]
A couple things:
The last assertion works! After $this->post('students/add', $data) is submitted, the $query is populated with data.
The first assertion does not work. I debug the Fixture before the $this->post() is called and it returns empty.
In the Test Table, there is a test for $_SESSION variable, which is what line 206 referring to.
Long Story short: The Fixture is not populated with data during the start of the Test Case, but once the integration runs then the Fixture magically contains all the data. I get $_SESSION errors, but I already set the session in the setUp(), so I'm lost.
I greatly appreciate any help. Thank you

I was able to by pass this message by setting the $_SESSION super global directly on my Test:
public function setUp() {
parent::setUp();
$this->loadFixtures(
'Students', 'Users');
// Configure Authentication
$this->session([
'Auth' => [
'User' => [
'id' => 21896,
'institution_id' => 1,
'student_id' => null,
'contact_id' => 91,
'email' => 'AuthenticatedEmail#school.edu',
'role' => 'DSP',
'is_admin' => false
]
]
]);
$_SESSION = [
'Auth' => [
'User' => [
'id' => 21896,
'institution_id' => 1,
'student_id' => null,
'contact_id' => 91,
'email' => 'AuthenticatedEmail#school.edu',
'role' => 'DSP',
'is_admin' => false
]
]
];

Related

Symfony - Error with the data parameter on a form

Context of the problem :
I created a symfony form.
Each tool has a collection of modules.
The user has a collection of modules of any tool.
What I want :
I want for each tool there are checkboxes corresponding to the tool's modules. The module checkboxes that the user owns are checked.
([] = checkbox)
Tool1 : []Module1 [x]Module2 [x]Module3
Tool2 : []Module4 [x]Module5
Tool3 : [x]Module6 []Module7
What I currently have:
For each tool, there are checkboxes corresponding to the tool's modules. But I have a problem to tick the checkboxes of user's modules. I get an error on the data parameter.
The form field :
$user = $options['user'];
$tools = $options['tools'];
foreach ($tools as $tool) {
$name = 'profile_'.str_replace(array('-', ' ', '.'), '', $tool->getLibelle());
$builder
->add($name, ChoiceType::class, [
'label' => $tool->getLibelle(),
'choices' => $tool->getModules(),
'choice_value' => 'id',
'choice_label' => function (?Module $module) {
return $module ? $module->getName() : '';
},
'data'=> $user->getModules(), // ERROR HERE
'expanded' => true,
'multiple' => true,
'mapped'=>false
])
;
}
[...]
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
'user'=> null,
'category'=> null,
'tools'=> null,
]);
}
The error :
My question :
Why do I have this error? How can I use the data parameter correctly to achieve the expected result?
You are on the good way, try to dump what is $user->getModules() returning, it has to be an array. May be is not returning an array, check te relation.
I did a little test and it works perfectly.
$name = 'name_field';
$builder->add($name,ChoiceType::class, array(
'choices' => array('Yes', 'No'),
'data' => array('Yes', false),
'mapped' => false,
'expanded' => true,
'multiple' => true
));
Here the solution :
Seems to me that $user->getModules() returns a collection. I managed to find another solution and that works (I changed the type of the field to EntityType)
foreach ($tools as $tool) {
$name = 'acces_'.str_replace(array('-', ' ', '.'), '', $tool->getLibelle());
$builder
->add($name, EntityType::class, [
'class'=> Module::class,
'label' => $tool->getLibelle(),
'data' => $user->getModules(),
'choices'=> $tool->getModules(),
'choice_value' => 'id',
'choice_label' => 'name',
'expanded' => true,
'multiple' => true,
'required' => true,
'mapped'=>false,
])
;
}
ChoiceType: data parameter need array
EntityType: data parameter need collection
Thanks for the help !

Use formBuilder in a foreach to generate multiple fields

I'm developing a project with Symfony 5, currently busy with an import of data from CSV to a database. It's requested to offer the client the possibility to choose, which data from the CSV is corresponding to which field of the database.
For example, I want to import users to the database.
The array of fields from the database:
$databaseFields = ['username', 'email', 'lastname', 'firstname'];
The array of headers, frome the CSV:
$headersCsvArray = ['email', 'username', 'lastName'];
Based on this 2 arrays, I need to build a form:
foreach ($databaseFields as $databaseField) {
$builder->add('extraFields', ChoiceType::class,
[
'label' => $databaseField,
'placeholder' => 'Choose a column from the excel file',
'choices' => $headersCsvArray,
'multiple' => false,
'expanded' => false,
'required' => false
]
);
}
To be clear, I need an input for each field of the User entity, with a dropdown with every header from the CSV.
This form is link to an ImportUserFormModel, with basically nothing for the moment:
class ImportUserFormModel
{
public $extraFields;
}
The result I need when I do $form->getData('extraData'):
$importDatas = [
'username' =>
[
'username' => true,
'emails' => false,
'lastName' => false
],
'email' =>
[
'username' => false,
'emails' => true,
'lastName' => false
],
'lastname' =>
[
'username' => false,
'emails' => false,
'lastName' => true
],
'firstname' =>
[
'username' => false,
'emails' => false,
'lastName' => true
],
];
Where the TRUE value is the one choose from the select dropdown.
Here is the result I actually have:
So I'm asking myself: Is it possible to use the foreach here?
Ok if you want it dynamic this could be a solution.
Create a Model for your extra field too, like
class ExtraField {
private $databseField;
private $csvField;
// ...
}
And in your base model use it as a collection
class ImportUserFormModel
{
private $extraFields;
public getFields(): ExtraField[] {
// ...
}
public addField(ExtraField $extraField) {
// ...
}
}
Create a FormType for your ExtraField model and another one for your base model where extraFields is a CollectionType.
Then before the form creation define the fields in your model
$importUserFormModel = new ImportUserFormModel();
foreach ($databaseFields as $databaseField) {
$extraField = new ExtraField();
$extraField->setDatabaseField($databaseField);
$importUserFormModel->addExtraField($extraField);
}
$form = $this->createForm(ImportUserFormModelType::class, $importUserFormModel);
// ....
The thing i'm not sure about is how to put the name of the database field in the label of the form field but you could template it.

How can I use UploadField within GridFieldEditableColumns in SilverStripe 4?

I have a problem with GridFieldEditableColumns. I extended EditableOption with a list of files, and now I want the CMS columns to display an upload field. I took the EditableMultipleOptionField as a reference and implemented it like this:
$editableColumns = new GridFieldEditableColumns();
$editableColumns->setDisplayFields([
'Title' => [
'title' => 'Beschreibung',
'callback' => function ($record, $column, $grid) {
return TextField::create($column);
}
],
'Value' => [
'title' => 'Wert',
'callback' => function ($record, $column, $grid) {
return TextField::create($column);
}
],
'Default' => [
'title' => 'Standardmäßig aktiv?',
'callback' => function ($record, $column, $grid) {
return CheckboxField::create($column);
}
],
'Examples' => [
'title' => 'Beispielbilder',
'callback' => function ($record, $column, $grid) {
return UploadField::create($column);
}
]
]);
However, I always get errors when I use UploadField:
[Emergency] Uncaught LogicException: Field must be associated with a form to call Link(). Please use $field->setForm($form);
I also tried it with ['field' => UploadField::class], same problem. The extension works, because if I change the field for the DataObject to type Text and use a TextField instead, it works fine. How can I use an UploadField within GridFieldEditableColumns?

Symfony4 - change single parameter in url after submitting form

I have a route that looks like this - route/{parameter} and I need to change the parameter after submitting a form.
I tried to use redirectToRoute but it created new URL together with some other parameters that the form passed which I don't want.
So I would love to ask you if there is some way to redirect to a new URL with the only parameter that I choose through select in the form.
Thank you very much for your responses.
EDIT:
I am going to share more actual information. This is how my controller for the form looks like:
$form = $this->createFormBuilder()
->setMethod("get")
->add('category', ChoiceType::class, [
'choices' => [
'Všechny kategorie' => 'vsechny-kategorie',
'Automobilový průmysl' => 'automobilovy-prumysl',
'Stavebnictví' => 'stavebnictvi',
'Elektronika a elektrotechnika' => 'elektronika-a-elektrotechnika',
'Gastronomie' => 'gastronomie',
'Lesnictví' => 'lesnictvi',
'Potravinářský průmysl' => 'potravinarsky-prumysl',
'IT technologie' => 'it-technologie',
'Logistika' => 'logistika',
'Strojírenství' => 'strojirenstvi',
'Zdravotnictví' => 'zdravotnictvi'
],
'label' => 'Kategorie:'
])
->add('send', SubmitType::class, ['label' => 'Test'])
->getForm();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$category = $data['category'];
return $this->redirectToRoute('jobs', [
'jobs' => $pagination,
'categoryForm' => $form->createView(),
'category' => $category,
]);
}
You should be able to use the redirectToRoute, but be sure to pass the parameter you're trying to dynamically set as an array:
// in your controller action:
return $this->redirectToRoute('post_form_route', ['parameter' => $parameter]);
If that's not working for you, I would double check your route definitions and make sure your route's name & expected URL parameters are passed correctly.
Documentation on redirecting in the controller
you can try that :
if($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$category = $data['category'];
return $this->redirectToRoute('route', [
'parameter' => $form->getData()->getCategory()
]);
}
return $this->redirectToRoute('jobs', [
'jobs' => $pagination,
'categoryForm' => $form->createView(),
]);

Cakephp 3.x Saving multiple tables

In my cakephp (3.3) project, How to save multiple tables in a single save. My association is as follows, also I am giving my code here.
Table1 : users
In class UsersTable extends Table
$this->hasOne('ProfileDetails', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
Table2 : profile_details
In class ProfileDetailsTable extends Table
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
What should be the data format to save multiple tables?
In my template I have added the following , for now I have used only few fields
$this->Form->input('User.username',['type' => 'text','class'=>'form-control']) ?>
$this->Form->input('User.password',['type' => 'password','class'=>'form-control']) ?>
$this->Form->input('User.email',['type' => 'text','class'=>'form-control']) ?>
$this->Form->input('ProfileDetail.first_name',['type' => 'text','class'=>'form-control']) ?>
In the controller before saving i have debuged which gives result as follows
debug($this->request->data);
$user = $this->Users->patchEntity($user, $this->request->data);
debug($user);
exit;
$this->Users->save($user)
Result of debug
\src\Controller\UsersController.php (line 39)
[
'User' => [
'username' => 'Tester',
'password' => '43434324',
'email' => 'test#gmail.com',
'role' => 'admin'
],
'ProfileDetail' => [
'first_name' => 'Tester'
]
]
\src\Controller\UsersController.php (line 41)
object(App\Model\Entity\User) {
'User' => [
'username' => 'Tester',
'password' => '43434324',
'email' => 'test#gmail.com',
'role' => 'admin'
],
'ProfileDetail' => [
'first_name' => 'Tester'
],
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'User' => true,
'ProfileDetail' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [
'email' => [
'_required' => 'This field is required'
]
],
'[invalid]' => [],
'[repository]' => 'Users'
}
Could you please suggest me how to save this data in multiple tables with single save with validation?
Looking at your debug, there is an error saying the email field is required. Maybe that is your problem..
If still not working , try this:
replace User by users
and ProjectDetail by project_details
In your form. And also remove joinType from following:
$this->hasOne('ProfileDetails', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);

Resources