Symfony2 tree builder - what does the method canBeUnset() do? - configuration

$rootNode
->children()
->arrayNode('form')
->info('form configuration')
->canBeUnset()
->treatNullLike(array('enabled' => true))
->treatTrueLike(array('enabled' => true))
->children()
->booleanNode('enabled')->defaultTrue()->end()
->end()
->end()
Line 5 of the above snippet from Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration uses the method canBeUnset(). I don't know what this does because it seems to not do anything if I remove it. I'm working understanding semantic configuration for my own bundles.

Following the code, you can find definition for this method in Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition class.
/**
* Sets whether the node can be unset.
*
* #param Boolean $allow
*
* #return ArrayNodeDefinition
*/
public function canBeUnset($allow = true)
{
$this->merge()->allowUnset($allow);
return $this;
}
This is passed to MergeBuilder ( Symfony/Component/Config/Definition/Builder/MergeBuilder ) which handles config merging.
/**
* Sets whether the node can be unset.
*
* #param Boolean $allow
*
* #return MergeBuilder
*/
public function allowUnset($allow = true)
{
$this->allowFalse = $allow;
return $this;
}
So my understanding is, that this method defines, if your config value can be unset while merging configurations, in case the overriding config does not support the value. I would have to test though, to find out the behaviour if the unsetting is not allowed, but I guess then it would throw an exception about a missing config value just like isRequired.

Related

Doctrine: addSelect method creates undesired seperate object (Symfony)

I want to query the entity based on a calculated property that does not exist in the database or on the entity.
If I run
return $this->createQueryBuilder('b')
->select('b')
->addSelect(
'... as extra_property'
)
->having('extra_property = :param')
->setParameter('param', $param)
->orderBy('extra_property', 'ASC')
->getQuery()
->getResult();
This results in a collection with each entity in the following format:
"0": {}, // The 9 entity properties
"extra_property": "value"
However, I want the extra_property to be added to the other entity properties as the tenth property. How do I fix this?
The problem lies in the getResult() method, the default hydration method is "HYDRATE_OBJECT" which will try to hydrate the output as the defined entity, which does not know about your extra property.
public function getResult($hydrationMode = self::HYDRATE_OBJECT)
{
return $this->execute(null, $hydrationMode);
}
Depending on what your other properties are, you could just use the "HYDRATE_SCALAR" option which will just give you a flat output of the results.
If you other properties are nested entities, you will have to manually select the fields in your select to bypass the hydration process, or find a way to add that property to your entity and tell the hydrator to get that data from a dedicated query or something.
You can find the different hydration methods as constants of the AbstractQuery class, you can read more on the Doctrine documentation
/**
* Hydrates an object graph. This is the default behavior.
*/
public const HYDRATE_OBJECT = 1;
/**
* Hydrates an array graph.
*/
public const HYDRATE_ARRAY = 2;
/**
* Hydrates a flat, rectangular result set with scalar values.
*/
public const HYDRATE_SCALAR = 3;
/**
* Hydrates a single scalar value.
*/
public const HYDRATE_SINGLE_SCALAR = 4;
/**
* Very simple object hydrator (optimized for performance).
*/
public const HYDRATE_SIMPLEOBJECT = 5;
/**
* Hydrates scalar column value.
*/
public const HYDRATE_SCALAR_COLUMN = 6;

MYSQL - How to search in JSON array?

On my Symfony 5 app, i've a database with a candidate table that contains a json field.
candidate 1 : [{"end": "30/04/2020", "start": "01/03/2020"},{"end": "31/07/2020", "start": "01/07/2020"}]
candidate 2 : [{"end": "31/03/2020", "start": "01/03/2020"},{"end": "31/07/2020", "start": "01/07/2020"}]
Is it possible with query builder to find a candidate where this field corresponds to the arguments ?
ex: I would like to find all the candidates who are available between 10/03/2020 and 10/04/2020.
This case should just return the candidate 1.
I guess it's not possible to do this with query builder so i'm trying to use native SQL but... what's the sql syntax ?
I tried with availability_dates`->"$.start" = "01/03/2020" but it does not work because it's a "collection".
This is a poorly-conceived database structure. Clearly, the JSON string represents a "repeating group" of related data, which violates the principles of so-called "normal forms."
https://en.wikipedia.org/wiki/Database_normalization
You should be storing the start/end dates in a separate table, say, candidate_dates, with columns like candidate_id, start, end. This has a so-called "one-to-many relationship" to the parent table, candidates.
Now, you can write a simple query which JOINs the two tables to get the answers you need.
Entity like that ?Entity like that ?
One candidate can have one or more available dates and one available dates can only be linked to one candidate.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Table(name="candidate_available_dates", uniqueConstraints={
* #ORM\UniqueConstraint(name="unique_candidate_available_dates", columns={"candidate_id", "start", "end"})
* })
*
* #ORM\Entity(repositoryClass="App\Repository\CandidateAvailableDatesRepository")
*/
class CandidateAvailableDates
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Candidate", inversedBy="candidateAvailableDates")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="candidate_id", referencedColumnName="candidate_id", nullable=false)
* })
*/
private $candidate;
/**
* #ORM\Column(type="date")
* #Assert\NotBlank
*/
private $start;
/**
* #ORM\Column(type="date")
* #Assert\NotBlank
*/
private $end;
[...]
// GETTER and SETTER
And in Candidate entity, the reversed side
/**
* #ORM\OneToMany(targetEntity="App\Entity\CandidateAvailableDates", mappedBy="candidate")
*/
private $candidateAvailableDates;

Doctrine findBy boolean field returns no results

Recently, a piece of code stopped working. I haven't made any changes to it so I don't know why.
Here's the code:
$invites = $this->vault_em->getRepository('AppBundle:Invite\LocalInvite')->findBy([
'active' => true,
]);
Now, it's returning an empty array, even though there are LocalInvite records with active = 1.
Here are the doctrine mappings:
/**
* #ORM\Entity
* #ORM\Table(name="invite")
*/
class LocalInvite extends Invite {
//...
}
/** #ORM\MappedSuperclass */
abstract class Invite implements \JsonSerializable {
/** #ORM\Column(type="boolean", options={"default": true}) */
protected $active;
//...
}
To debug, I copied the underlying MySQL query that Doctrine is executing from the debug logs:
SELECT t0.id AS id_1, t0.email AS email_2, t0.active AS active_3, t0.location AS location_4, t0.cohort_leadership AS cohort_leadership_5, t0.timezone AS timezone_6, t0.date_record_created AS date_record_created_7, t0.date_record_deleted AS date_record_deleted_8, t0.date_restart AS date_restart_9, t0.date_start_invite AS date_start_invite_10, t0.employee_id AS employee_id_11, t0.first_name AS first_name_12, t0.corporate_client_name AS corporate_client_name_13, t0.client_id AS client_id_14, t0.landing_page_url AS landing_page_url_15, t0.user_id AS user_id_16, t0.recipient_id AS recipient_id_17 FROM invite t0 WHERE t0.active = true;
When I plug that query into a MySQL IDE, it returns results.
Why does the findBy return no results?
try to change 'AppBundle:Invite\LocalInvite' by LocalInvite::class

twig json_encode empty json

I'm receiving a list of elements from an API. All the elements are well formatted. When I dump one of them using twig, I get the following :
Leg {#2695 ▼
-id: null
#reservation: null
-airportStart: "AIX LES MILLES"
-airplaneType: "Cessna Citation Mustang"
-airportEnd: "ROBINSON"
-startDate: "2015-09-10 20:00:00"
-startHour: "2015-09-10 20:00:00"
-endHour: "2015-09-10 21:00:21"
-durationLeg: "01:21"
#nbPax: "4"
-price: null
-updatedPrice: null
-discountOnLeg: null
-tva: null
-status: null
}
My user must select one of these elements, So what I'm trying to do is to send the encoded json back to the controller, using
{{ element|json_encode }}
Unfortunately, the json is empty. When I try to dump the encoded json using
{{ dump(element|json_encode) }}
all I get is an empty array {};
Any idea why Is there another way to send the selected element datas to a controller function? (These elements are not persisted, each call on the API returns thousands of results)
I'm little late to the party (2 years of lateness), but for any one like me coming from a google research i say : i had the same problem too, and after googling around, i "solved" my problem with the Serializer Component. How? let me show you!
Installation
php composer.phar require symfony/serializer
Entity
<?php
namespace Your\Namespace\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
/**
* Leg
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Your\Namespace\Entity\LegRepository")
*/
class Leg {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
public function serializer()
{
$encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array(
'whatever', 'attributes', 'you', 'want', 'to', 'ignore'
));
// The setCircularReferenceLimit() method of this normalizer sets the number
// of times it will serialize the same object
// before considering it a circular reference. Its default value is 1.
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
$serializer = new Serializer(array($normalizer), array($encoder));
return $serializer->serialize($this, 'json');
}
}
Twig
{{ Leg.serializer|raw }}
N.B : this is tested under symfony 2.6

Doctrine2 multiple join

I have problem with my User Entity. I have code generated by Doctrine it is below:
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Frontend\UserBundle\Entity\SfGuardPermission", inversedBy="user")
* #ORM\JoinTable(name="sf_guard_user_permission",
* joinColumns={
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="permission_id", referencedColumnName="id")
* }
* )
*/
protected $permission;
Problem is with join because I can't join user and permission. What I must to do? I must join sf_guard_user with sf_guard_user_group with sf_guard_grop with sf_guard_group_permission with sf_guard_permission. Because I need to get User permission. I do not no how to write join like this in code above. Is it possible?
You can not write this join in one annotation. In fact you gone have three entity tables sf_guard_user, sf_guard_group and sf_guard_permission and two cross tables which you can write as you already started, sf_guard_user_group and sf_guard_group_permission.
But since it looks like you try to migrate some symfony 1.x stuff to symfony 2.x:
The sf_guard_user_permisson table in symfony 1.x is a cross table between users and permission, containing extraordinaire permission for a user which are not granted through the groups the user is in, so you are already done.
SBH thx for replay, of course you have right with everything what you have written. But my sf_guard_user_permisson is empty so I can't use it. I can generate this table, this is no problem, but then I will must maintain it. This is next work for me so i wrote code below:
namespace Frontend\UserBundle\Entity;
// ...
/**
* #var \Doctrine\Common\Collections\Collection
*
*/
protected $permissions;
/**
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPermissions()
{
$groups = $this->getSfGuardGroups();
foreach ($groups as $group)
{
$groupPermisions = $group->getPermission();
foreach ($groupPermisions as $groupPermision)
{
if (!in_array($groupPermision, $this->permissions)) {
$this->permissions[] = $groupPermision;
}
}
}
return $this->permissions;
}
/**
* #param string $permissionName
* #return boolean
*/
public function hasPermission($permissionName)
{
$this->getPermissions();
foreach ($this->permissions as $permission)
{
if($permission->getName() === $permissionName) {
return true;
}
}
return false;
}
// ..
What do you think about it? Your opinion is very important for me.
Edit:
Thx for SBH help, I have got answer for my question. I have hope it will help other people. If you do not understand something please look at SBH answer.