This annotation of a constraint works:
use App\Api\Dto\DtoInterface;
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Annotations as OA;
use Symfony\Component\Serializer\Annotation\Groups as SerializerGroups;
use Symfony\Component\Validator\Constraints as Assert;
class Report implements DtoInterface
{
/**
* #OA\Property(description="visited house id SAP format 4 character string", type="string")
*
* #SerializerGroups({"create", "update", "view", "collection"})
*
* #Assert\NotBlank
* #Assert\Length(4)
*/
public string $house = '';
and this doesn't
use App\Api\Dto\DtoInterface;
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Annotations as OA;
use Symfony\Component\Serializer\Annotation\Groups as SerializerGroups;
use Symfony\Component\Validator\Constraints as Assert;
class Report implements DtoInterface
{
/**
* #OA\Property(description="visited house id SAP format 4 character string", type="string")
*
* #SerializerGroups({"create", "update", "view", "collection"})
*
* #Assert\NotBlank(groups={"create", "update"})
* #Assert\Length(min=4, groups={"create", "update"})
*/
public string $house = '';
Lucky for me in this case ignoring groups will still work out for me, but in other cases it might not.
The Symfony documentation says that this is how it should work.
What is wrong with my second example? Why are those validators ignored?
Please note that in the second detection, #Assert\NotBlank(groups={"create", "update"}) sterilization groups were detected in the validators, in contrast to the first example, which means that they are presented only to the given groups, and all other requests with unspecified loads are ignored.
In other words, the restrictions in the second example will only work for create and update, and for the view and collection groups they will be ignored, since they are not specified in the annotations.
Related
I'm searching a way to set the default value of a boolean filter (without setting the filter via GET-Parameters).
To make my question clearer: Instead of using this request
http://localhost/api/myclass?archived=0
I just want to get all non-archived objects by:
http://localhost/api/myclass
So I want to set a standard filter set (like I can also set a standard order filter set).
My favorite way is to use annotations. I couldn't find it in the api platform bundle docu.
<?php
namespace AppBundle\Entity\MyClass;
use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\BooleanFilter;
/**
* MyClass
*
* #ORM\Table(name="my_class")
* #ORM\Entity(repositoryClass="AppBundle\Entity\MyClass")
*
* #ApiResource(
* attributes={
* "normalization_context"={"groups"={"api_read", "api_read_v2"}},
* "denormalization_context"={"groups"={"api_write"}}
* }
* )
* #ApiFilter(BooleanFilter::class, properties={"archived"})
*/
class MyClass
{ ...
/**
* #var bool|null
* #Groups({"api_read", "api_write"})
* #ORM\Column(name="archived", type="boolean", nullable=true)
*/
protected $archived = 0;
...
}
the BooleanFilter just allow you to filter your request by the value of your boolean.
For your problem maybe try something like this in annotation (without the nullable !) :
#ORM\Column(name="archived", type="boolean", options={"default":true})
I have to check a submitted form against the existing database to make sure it is not duplicated by the combination of several fields, say supplier_id, invoice_no and amount.
Is there a built-in method or should I write a code myself? If so - what are guidelines: where to put it, what are the good practices?
At the moment I am extending the CRUD controller and overwriting the createAction adding the condition there. Not sure whether this method is a good practice.
Example:
<?php
namespace AppBundle\Entity\User;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #UniqueEntity({"name", "email"}, message="This value is in a database.")
*/
class User
{
/**
* #var string
*/
protected $name;
/*
* #var string
*/
protected $email;
...
}
I have a mapped superclass AbstractQuestion with single-table-inheritance.
/**
* #ORM\Entity
* #ORM\MappedSuperclass
* #ORM\Table(name="Question")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="dtype", type="string")
* #ORM\DiscriminatorMap({
* "simple": "SimpleQuestion",
* "dropdown": "DropdownQuestion"
* })
*/
abstract class AbstractQuestion
SimpleQuestion and DropdownQuestion inherit from this superclass.
/**
* Class SimpleQuestion.
* #ORM\Entity()
*/
class SimpleQuestion extends AbstractQuestion
I want to modify an existing SimpleQuestion and make it a DropdownQuestion.
When saving a question, I deserialise and merge the question, which contains an ID and the 'dtype' and other properties.
$dquestion = $this->serial->fromJson($request->getContent(), AbstractQuestion::class);
$question = $this->em->merge($dquestion);
$this->em->flush();
So I submit something like:
{ id: 12, dtype: 'dropdown', 'text': 'What is my favourite animal?'}
After the deserialisation, $dquestion is a DropdownQuestion object as I desired, but after the merge $question is a SimpleQuestion object as it was in the database previously, so any unique properties of DropdownQuestion are lost and the question is saved as a SimpleQuestion. Is there any way to work around this?
You will first have to delete the existing record (SimpleQuestion) and then insert the new record (DropdownQuestion). Type casting is not supported in Doctrine 2.
Note.
You can probably change the discriminator column with a pure SQL query, but this is absolutely not recommended and will for sure give you problems...
Check also the answers here and here since they might be interesting for you.
Hi working on an admin system. I want to run the Validation Constraint "NotBlank" before running #SecurityAssert\UserPassword. (otherwise there is unneeded database hit and are two different error messages for the user) Is this possible? Thanks!
use Symfony\Component\Security\Core\Validator\Constraints as SecurityAssert;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Constraints\NotBlank;
class YourClassName
/**
* #Assert\NotBlank( message = "For security reasons, please enter your current password.")
* #SecurityAssert\UserPassword(
* message = "Wrong value for your current password"
* )
*/
protected $oldPassword;
As #Qoop said in his comment the use of a sequence group like the example below could suit your needs:
use Symfony\Component\Security\Core\Validator\Constraints as SecurityAssert;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* #Assert\GroupSequence({"YourClassName", "After"})
*/
class YourClassName
/**
* #Assert\NotBlank(
message = "For security reasons, please enter your current password.")
* #SecurityAssert\UserPassword(
* message = "Wrong value for your current password",
groups={"After"}
* )
*/
protected $oldPassword;
Remember to add these validation groups when you build the form.
If you need a solution for one or two fields you could use Validation Callback. You just validate a field for specified constraints in the method and call them in your order.
It is not possible to do constraints with priorities with Symfony validator out of the box. But you can write own wrapper for validation with all features that you need.
See https://github.com/markwilson/symfony2-validator-priority-chain for example.
In older versions of Symfony you used to be able to build new objects within a data object by extending a model class with an extended subclass.
For example, I had a questionnaire model that had a results table. That results table had a Result.php model class that used to set and get the results through Doctrine. I then used the ResultPeer.php model subclass to add a new function to the Result object that took the result and depending on a fixed set of thresholds calculated a score and corresponding colour.
In the new Symfony2 version using Doctrine2 I am struggling to work out the best way to do this. When creating an entity I can only find in the documentation the ability to add objects based on the data structure relationships.
I looked at the entity repositories, but that does not appear to extend or add functionality to an original object. It seems to bring back data objects based on queries that are more complex than the standard query functions.
I also looked at services, which I can use to collect the object and then using the object create a new array that includes this object and the newly created data, but this just does not seem right or follow the philosophy that Symfony is all about.
Does anyone know how functions can be added to an existing data object. I found it really useful in the older version of Symfony, but cannot seem to find the alternative in the new version of Symfony2.
Extending an entity is the way to go. In the Doctrine2 world, they talk about inheritance mapping. Here a code example. It defines a BaseEntity then it is extendsed to create a BaseAuditableEntity and finally there is a User entity extending BaseAuditableEntity. The trick is to use the #Orm\MappedSuperclass annotation. This inheritance scheme will create a single table even if there is three entities in my relationships graph. This will then merge all properties into a single table. The table created will contains every property mapped through the relations, i.e. properties from BaseAuditableEntity and from User. Here the code examples:
Acme\WebsiteBundle\Entity\BaseEntity.php
namespace Acme\WebsiteBundle\Entity;
use Doctrine\ORM\Mapping as Orm;
/**
* #Orm\MappedSuperclass
*/
class BaseEntity {
}
Acme\WebsiteBundle\Entity\BaseAuditableEntity.php
namespace Acme\WebsiteBundle\Entity;
use Doctrine\ORM\Mapping as Orm;
/**
* #Orm\MappedSuperclass
*/
class BaseAuditableEntity extends BaseEntity {
private $createdBy;
/**
* #Orm\Column(type="datetime", name="created_at")
*/
private $createdAt;
/**
* #Orm\ManyToOne(targetEntity="User")
* #Orm\JoinColumn(name="updated_by", referencedColumnName="id")
*/
private $updatedBy;
/**
* #Orm\Column(type="datetime", name="updated_at")
*/
private $updatedAt;
// Setters and getters here
}
Acme\WebsiteBundle\Entity\User.php
namespace Acme\WebsiteBundle\Entity;
use Acme\WebsiteBundle\Entity\BaseAuditableEntity;
use Doctrine\ORM\Mapping as Orm;
/**
* #Orm\Entity(repositoryClass="Acme\WebsiteBundle\Entity\Repository\UserRepository")
* #Orm\Table(name="acme_user")
*/
class User extends BaseAuditableEntity implements AdvancedUserInterface, \Serializable
{
/**
* #Orm\Id
* #Orm\Column(type="integer")
* #Orm\GeneratedValue
*/
private $id;
/**
* #Orm\Column(type="string", name="first_name")
*/
private $firstName;
/**
* #Orm\Column(type="string", name="last_name")
*/
private $lastName;
/**
* #Orm\Column(type="string", unique="true")
*/
private $email;
// Other properties
// Constructor
// Setters and getters
}
Here a link to the official inheritance mapping documentation of Doctrine 2.1: here
Hope this helps, don't hesitate to comment if you need more information.
Regards,
Matt