How can I obtain the values of an Entity in Laravel-Backpack? - laravel-5.3

While I was trying to show pictures I made this pleasant discovery in backpack:
$this->crud->addFields([
[ // Upload
'name' => 'pictures', //<-- this is an Entity
'label' => 'Photos',
'type' => 'upload_multiple',
'upload' => true,
'disk' => 'uploads'
]
]);
This fragment of code gives me this:
My questions is:
What am I doing wrong?
How can get the value 'file' of this two vectors? I need this for display the images.

You probably forgot to add 'photos' to the cast array.
Based on the documentation:
Step 3. Since the filenames are stored in the database as a JSON array, we're going to use attribute casting on your model, so every time we get the filenames array from the database it's converted from a JSON array to a PHP array:
protected $casts = [
'photos' => 'array'
];
https://laravel-backpack.readme.io/docs/crud-fields#upload_multiple

Related

Add special characters and number in password symfony

For my project, I have to create Users and I would like to change the properties of the password. Symfony automaticcaly define 6 characters for it, but i would like to add special characters and number in it. For the length i'm fine, but for the rest i don't find the answers.
Here is my form
->add('plainPassword', PasswordType::class, [
// instead of being set onto the object directly,
// this is read and encoded in the controller
'mapped' => false,
'attr' => ['autocomplete' => 'new-password'],
'constraints' => [
new NotBlank([
'message' => 'Please enter a password',
]),
new Length([
'min' => 6,
'minMessage' => 'Your password should be at least {{ limit }} characters',
// max length allowed by Symfony for security reasons
'max' => 4096,
]),
],
])
Do you know if I change the properties in my form it will be enough?
Thanks for your help.
Take a look at Compound constraint
So basically you would create your own "rules-set". In this case, for passwords.
In order to have at least one number (digit) or/and one special-char, you could use Regex Constrains. To be more precise - two of them:
to match at least one digit
to match at least one special char
// src/Validator/Constraints/MyPasswordRequirements.php
use Symfony\Component\Validator\Constraints\Compound;
use Symfony\Component\Validator\Constraints as Assert;
class MyPasswordRequirements extends Compound {
protected function getConstraints(array $options): array
{
return [
new Assert\NotBlank(),
new Assert\Type('string'),
new Assert\Length(['min' => 6]),
//regex -> to have at elast one digit
new Assert\Regex([
'pattern' => '/\d+/i',
]),
//regex -> to have at elast one special char from the list
//note: list of special-char is [#?!#$%^&*-]. Adjust to suite your needs
new Assert\Regex([
'pattern' => '/[#?!#$%^&*-]+/i',
]),
];
}
}
and then in your form just use this one MyPasswordRequirements constraint
->add('plainPassword', PasswordType::class, [
'mapped' => false,
'attr' => ['autocomplete' => 'new-password'],
'constraints' => [
new MyPasswordRequirements()
],
// validation error message
'invalid_message' => 'Password requirements are not fulfilled'
])
P.S. I intentionally split reg-ex in two, just for better understanding and demonstration. I can combine them into one, and perhaps add further restrictions. Take as look at Regex strong password the special characters

Why does Analytics Data API V1 Beta not conform to the REST spec?

I'm adding GTM and GA4 to some website apps that need to produce detailed stats on the click-throughs of ads per advertiser.
This looks infeasible with standard GA reporting so am using the PHP implementation of Analytics Data API V1 Beta. Since there are few examples (eg analyticsdata/quickstart.php) of V1 reporting using PHP, I am translating other classes and operands from the REST API’s JSON .
<?php
namespace Google\Analytics\Data\V1beta;
require 'vendor/autoload.php';
$property_id = '<redacted>';
putenv('GOOGLE_APPLICATION_CREDENTIALS=Keyfile.json');
$client = new BetaAnalyticsDataClient();
// Make an API call.
$response = $client->runReport([
'property' => 'properties/' . $property_id,
'dateRanges' => [
new DateRange([
'start_date' => '2021-04-01',
'end_date' => 'today',
]
),
],
'dimensions' => [new Dimension(
[
'name' => 'customEvent:link_classes'
]
),
],
'dimensionFilter'=>[new FilterExpression(
[
'filter'=>[new Filter(
[
'field_name' => 'customEvent:Classes',
'string_filter' => [new Filter\StringFilter(
[
'match_type'=> '1',
'value'=> 'AdvertA',
'case_sensitive'=> false
])]])]])],
'metrics' => [new Metric(
[
'name' => 'eventCount',
]
)
]
]);
etc
The Quickstart example works but has endless trouble when a dimensionFilter is added.
For example, match_type should be an enum of one of a few character strings (EXACT, CONTAINS and so on). The JSON definition of match_type only shows the strings (enum 'members') and not any associated values (which would usually be integers). The GA4 migration guide has an example with
"matchType": "BEGINS_WITH"
PHP doesn’t have ‘enum’ but the equivalent would be to select one string and assign it to match_type (vide above). Wrong: StringFilter falls over unless it is given an integer operand, presumably the ordinal number of the desired match in the enum match string (and is the first one 0 or 1?). My understanding of the JSON schema was that an 'enum' list simply restricted the result to one of the unique operands, with an optional check on the operand type. (By comparison, the Python enumerate function returns an object containing a list of pairs with the ordinal number of an operand preceding the operand).
Custom dimensions appear not to conform to the API’s JSON. In Analytics, I specify a custom dimension with a dimension Name of Classes and User Property/Parameter of link_classes**.
However... in the API, dimension Name has to be customEvent:link_classes and not customEvent:Classes. Otherwise it falls over with ‘Field customEvent:Classes is not a valid dimension’
This occurs also when defining field_name in a Filter within a Filter Expression.
So is the API dimension Name not the name of the Analytics dimension Name but actually the Property/Parameter of an Analytics descriptive name? In one place I read the latter: "Custom dimensions are specified in an API report request by the dimension's parameter name and scope." but elsewhere it is implied that Name is the dimension name, e.g. /devguides/reporting/data/v1/advanced:
"dimensions": [{ "name": "customUser:last_level" }]
Finally, even falling in line with what the developers have implemented, dimensionFilter falls over with ‘Expect Google\Analytics\Data\V1beta\Filter\StringFilter’
It is Beta code but one would not expect overt deviations from the REST spec so perhaps I am reading the spec wrongly. Does anyone else have this problem?
** GTM has a ‘Click - Just Links’ trigger where the ‘click URL’ ‘contains’ the advertiser’s URL. The Classes custom dimension in the API dimension Filter has the class values of the adverts click-through links.
To answer the first part of your question, I believe the correct way to use an enum in PHP would be:
'match_type' => Filter\StringFilter\MatchType::BEGINS_WITH
As for the second question. Per the API schema documentation the name of a custom dimension is constructed as customEvent:parameter_name for event scoped dimensions and customUser:parameter_name for user scoped dimensions.
Where parameter_name, as you correctly noted, is not a descriptive name, but rather the event parameter name. In your example you seem to be using a user scoped dimension, so the dimension name in the API should be customUser:link_classes.
Here is a complete example that seems to be running fine:
require 'vendor/autoload.php';
use Google\Analytics\Data\V1beta\BetaAnalyticsDataClient;
use Google\Analytics\Data\V1beta\DateRange;
use Google\Analytics\Data\V1beta\Dimension;
use Google\Analytics\Data\V1beta\FilterExpression;
use Google\Analytics\Data\V1beta\Filter;
use Google\Analytics\Data\V1beta\Metric;
/**
* TODO(developer): Replace this variable with your Google Analytics 4
* property ID before running the sample.
*/
$property_id = 'YOUR-GA4-PROPERTY-ID';
$client = new BetaAnalyticsDataClient();
// Make an API call.
$response = $client->runReport([
'property' => 'properties/' . $property_id,
'dateRanges' => [
new DateRange([
'start_date' => '2020-03-31',
'end_date' => 'today',
]),
],
'dimensions' => [new Dimension(
[
'name' => 'customUser:link_classes'
]),
],
'dimensionFilter' => new FilterExpression(
[
'filter' => new Filter(
[
'field_name' => 'customUser:link_classes',
'string_filter' => new Filter\StringFilter(
[
'match_type' => Filter\StringFilter\MatchType::BEGINS_WITH,
'value' => 'AdvertA',
'case_sensitive' => false
]
)
])
]),
'metrics' => [new Metric(
[
'name' => 'eventCount',
]
)
]
]);
Many thanks Ilya for a most useful, prompt and correct reply.
Three points:
1.
Using
'match_type' => Filter\StringFilter\MatchType::BEGINS_WITH
instead of:
'match_type' => ‘BEGINS_WITH’
fixes the problem of “Uncaught Exception: Expect integer. in /vendor/google/protobuf/src/Google/Protobuf/Internal/GPBUtil.php” as the
MatchType::BEGINS_WITH (etc) constant returns an integer (in this case 2) from class MatchType.
2.
It would forestall errors if a reminder were added to the places in Custom Dimension documentation where dimension Name is used, such as
/devguides/reporting/data/v1/advanced: "dimensions": [{ "name": "customUser:last_level" }]
emphasising that name is not the dimension Name as defined to Analytics but rather the associated User Property/Parameter name. Or perhaps the Name heading in GA's Custom Dimension 'form' should be amended.
3.
Finally, dimensionFilter falling over with
‘Expect Google\Analytics\Data\V1beta\Filter\StringFilter’
error message was caused by my stupidity in instantiating FilterExpression, Filter and StringFilter as if they were array elements,
e.g
'string_filter' => [new Filter\StringFilter(
rather than
'string_filter' => new Filter\StringFilter(
something that is an unfortunately easy mistake to make when carelessly following the sample report code, where dateRanges, dimensions and so on correctly define arrays (since they can take multiple date ranges and dimensions), e.g.
'dimensions' => [new Dimension (

How do I change the single valued ChoiceType field to multi-value field without affecting existing data?

Currently, I have a Choicetype form field with a single value. But now I want it to be multi-valued. I added multiple => true, for that FormType, but it's giving an error like below:
Uncaught PHP Exception Symfony\Component\Form\Exception\TransformationFailedException: "Unable to transform value for property path "[facebook_lead_id]": Expected an array." at /app/vendor/symfony/form/Form.php line 1087 {"exception":"[object] (Symfony\Component\Form\Exception\TransformationFailedException(code: 0): Unable to transform value for property path "[facebook_lead_id]": Expected an array. at /app/vendor/symfony/form/Form.php:1087, Symfony\Component\Form\Exception\TransformationFailedException(code: 0): Expected an array. at /app/vendor/symfony/form/Extension/Core/DataTransformer/ChoicesToValuesTransformer.php:42)"} []
Any idea on how to convert single-valued to multiple values without impacting existing data.
To achieve the multi select you want, you must set the properties multiple and expanded to true.
$builder->add('fruits', ChoiceType::class, [
'multiple' => true,
'expanded' => true
'choices' => [
'bananas' => 1,
'apples' => 2,
'oranges' => false,
],
]);
Details here.

How to convert an integer based entity field into a human readable name in Twig

In a Symfony 2.4+ project, what is the best way to register an array of values for a field that saves as an integer but needs to display human readable values in a template?
I have an entity with a property that is populated with integer values that represent different constant values:
/**
* The repetition frequency for billing cycle:
* #ORM\Column(type="smallint")
*/
protected $repetition = 0;
I would like to store the names of these values somewhere, so initially I put them in my entity with a getter:
protected $repetitionName = array(
0 => 'Setup',
1 => 'Second',
2 => 'Minute',
3 => 'Hour',
4 => 'Day',
5 => 'Week',
6 => 'Month',
7 => 'Year'
);
public function getRepetitionName() {
return $this->repetitionName;
}
This seems like a great central repository for the values.
Then in my twig template I don't want to display the integer, I want the corresponding name value. So I translate them like this:
<div class="billingCycle">{{ entity.repetitionName[entity.repetition] }}</div>
And in my form builder I make a field that references that array like this:
$builder->add('repetition', 'choice', array(
'label' => 'Billing Cycle',
'help' => 'The repetition frequency when this service is billed.',
'choices' => $builder->getData()->getRepetitionNamePer(),
// default to monthly (the most common)
'empty_data' => 6,
'required' => TRUE
));
The Problems With This Approach
1. Translation: If I want to translate this at any point, it's hard coded in one place.
2. Reusability: If I have other entities that have repetition (e.g. event calendar) it would be nice to reuse this.
3. Configurability: Ideally these would be editable in a config file instead of the entity code.
Alternative Solution: Custom Form Type as Global Service with Config Parameters
The better option seems to set some default parameters in the config file:
parameters:
gutensite_component.options.status:
0: Inactive
1: Active
gutensite_component.options.repetition:
0: Setup
1: Second
2: Minute
3: Hour
4: Day
5: Week
6: Month
7: Year
Then create a custom form type Gutensite\ComponentBundle\FormType\RepetitionType that loads the options from the config parameters. See the Documentation for a great example of this. Then just refer to that field type like this:
$builder->add('repetition', 'repetition', array(
'label' => 'Billing Cycle',
'help' => 'The repetition frequency when this service is billed.',
// default to monthly (the most common)
'empty_data' => 6,
'required' => TRUE
));
The inconvenient part of this solution, is that you have to either parameter to twig in your config (which is bloat for every template even if you don't need it), or always remember to manually pass the parameters to twig from your controller.
// I add to the standard object `$controller->view` which gets passed to Twig
$controller->view->options['repetition'] = $this->container->getParameter('gutensite_component.options.repetition');
To be accessed like:
<div class="priceValue label label-primary">${{ entity.price }}/<span class="billingCycle">{{ view.options.repetition[entity.repetition] }}</span></div>
This is more clunky than I would like but it is reusable. Maybe others have better solutions to pass the configuration to twig than what is represented here.
Other Suggestions?
Do you have any other suggestions, best practices or lessons learned? Please share.
What I would do is to save those values as objects of a new entity and set a manyToOne relation from your main entity (or any other entity as your calendar event entity) to the repetition entity
If so, you can easily add new repetitions every time you want or get all objects of the main entity using any specific repetition.
Also you can build a nice form for a new object of the main entity with an input (human readable) of repetition values:
$builder->add('repetition', 'entity', array(
'class' => 'AcmeDefaultBundle:Repetition', // This is your new 'repetition entity'
'property' => 'repetitionName', // The field of your 'repetition entity' that stores the name (the one that will show a human readable value instead of the id)
'expanded' => true, // True if you prefer checkbox instead of a dropdown list
'label' => 'Billing Cycle',
'help' => 'The repetition frequency when this service is billed.',
'required' => true,
));
Now when you want to show the name of the repetition in twig you will use:
<div class="billingCycle">{{ entity.Repetition.repetitionName }}</div> (you can also translate that value later)
A cleaner way (if you use it only for this entity) is to define a public method in your entity that returns you that array:
public function getRepetitionNames() {
return array(
0 => 'Setup',
1 => 'Second',
2 => 'Minute',
3 => 'Hour',
4 => 'Day',
5 => 'Week',
6 => 'Month',
7 => 'Year'
);
}
Another method to retrieve the label based by your "repetition" field value:
/**
* #param $key
* #return null
*/
function getRepetitionLabel(){
$repetitions = $this->getRepetitionNames();
return isset($repetitions[$this->repetition]) ? $repetitions[$this->repetition] : 0;
}
Will be much easier to access it in twig now:
<div class="billingCycle">{{ entity.repetitionLabel }}</div>
Last, in your form use your entity to retrieve the values:
$object = $builder->getData();
$repetitionChoices = $object->getRepetitionNames();
$builder->add('repetition', 'choice', array(
'label' => 'Billing Cycle',
'help' => 'The repetition frequency when this service is billed.',
'choices' => $repetitionChoices
'empty_data' => 6,
'required' => TRUE
));
If you plan to use it in more entities, you could use a interface.

Symfony 2, how to persist join table entities?

This is such a trivial problem that I can't believe I couldn't find an answer.
Symfony 2, doctrine 2.1. I've got two entities and one intermediate entity (join table). User, Pref, and UsersPrefs. Pref table is dictionary table, so that I could change pref name in one place only. Ok, let's see the picture:
infographic http://dl.dropbox.com/u/22495762/infographic.png
As You can see, I want to have a checkbox group, with all the possible choices (prefs) and preferred choices checked. So, if there are 3 prefs, and only 2 selected by the user, there should be 3 checkboxes, 2 selected.
It's simple, if done plain PHP - query database twice to get list of all prefs and user prefs, render checkboxes depending on values, add some actions to handle form submit, done.
But for the life of God I can't get this to work using symfony & doctrine. I was able to get to the point where I can update relationships in doctrine and further in database, but I'm using raw query values for that:
$data = $request->request->get('some_form');
and this supposedly isn't the way it should be done?
Morevoer, I'm completely stuck as to how should I display checkbox list. I either get list of all options, none checked, or only user options, all checked. Or 'left joined' result set with checkboxes for all cases, useless anyway.
I've come to the point where I tried to overload twig checkbox template, but I couldn't pass variables to the form template, and that was the last straw...
EDIT:
This way I'm getting group of checkboxes, not connected to user choices:
->add('prefs', 'entity', array(
'class' => 'Some\TestBundle\Entity\Pref',
'expanded' => 'true',
'multiple' => 'true',
'property' => 'name'
))
And this way I'm getting only user choices, all checked:
->add('prefs', 'entity', array(
'class' => 'Some\TestBundle\Entity\UserPrefs',
'multiple' => 'false',
'expanded' => 'false',
'property' => 'pref.name',
'query_builder' => function(EntityRepository $er) use ($id) {
return $er->createQueryBuilder('u')
->where("u.user = :id")
->setParameter('id', $id)
;
},
))
And I tried left joins and other options, but at best I could get list of all possible options for all possible users, checked accordingly.
EDIT (POSSIBLE SOLUTION):
I'm displaying checkbox group:
->add('pref_ids', 'choice', array(
'choices' => array(
'1' => 'pref one',
'2' => 'pref two',
'3' => 'pref three',
),
'expanded' => 'true',
'multiple' => 'true'
))
I've added $pref_ids array in User entity. Now I just need to set values in array according to preferences chosen by user:
public function setPrefIds()
{
$prefs = $this->getPrefs();
$this->pref_ids = array();
foreach($prefs as $pref){
array_push($this->pref_ids, $pref->getPref()->getId());
}
return $this;
}
This way I get appropriate checkboxes checked.
Writing values to database is reversal of the process. I'm getting input values from request:
$data = $request->request->get('edit_form');
var_dump($data['pref_ids']);
Removing all user prefs:
foreach ($userPrefs as $pref){
$em->remove($pref);
}
And setting actual associations in doctrine from ids:
$entity->setPrefsById($em, $data['pref_ids']);
Here I'm passing entity manager to entity itself, but I need to refactor it, because it looks kinda messy this way.
Then $em->flush(); and that's it.
That's the best I could come up with. Probably it's overcomplicated and should be done entirely different way. Unfortunately couldn't figure out this "other way".
You need the choice field type: http://symfony.com/doc/current/reference/forms/types/choice.html
In your builder, it will be something like
$builder->add('prefs', 'choice', array('multiple' => true, 'expanded' => true, 'choices' => fetch the available prefs from the database here);
Edit: Sorry I'm mistaken, you need the "Entity" type, which fetches automatically the choices from the database: http://symfony.com/doc/current/reference/forms/types/entity.html
You must still put multiple => true expanded => true to get checkboxes.

Resources