Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed last year.
Improve this question
I created a little example:
class Response
{
/**
* #OA\Property(ref=#Model(type=EncodedModel::class))
*/
public RuntimeClass $filters;
}
class RuntimeClass implements JsonSerializable
{
public int $field;
public function jsonSerialize()
{
$model = new EncodedModel();
$model->field = "$this->field";
return $model;
}
}
class EncodedModel {
public string $field;
}
I have an endpoint which response is of model Response::class.
Response class has a field $filters of type RuntimeClass BUT when json encoded, due to JsonSerializable implemented function, it is displayed as a class EncodedModel.
My goal is to obtain in the generated api doc, that the field Response->filters is of type EncodedModel and not RuntimeClass.
I have annotated the field directly with #OA\Property(ref=#Model(type=EncodedModel::class)) and it does the trick but i don't like it so much.
As i see it, it is a RuntimeClass class responsability tell that json encoded results as a EncodedModel class and not a RuntimeClass class. Otherwise everytime i use RuntimeClass as a model field i have to remember to annotate it.
What i was hoping is that annotating the class like this would do the trick as well once for all, but it doesn't :/
/**
* #Model(type=EncodedModel::class)
*/
class RuntimeClass implements JsonSerializable
...
Anyone know if it is possible some way?
Thank you
----------------------- EDIT
Sorry i solved myself, i was about at the solution, just wrong annotation:
/**
* #OA\Schema(ref=#Model(type=EncodedModel::class))
*/
class RuntimeClass implements JsonSerializable
...
Thank you anyway
Sorry i solved myself, i was about at the solution, just wrong annotation:
/**
* #OA\Schema(ref=#Model(type=EncodedModel::class))
*/
class RuntimeClass implements JsonSerializable
...
Related
I have a repository class for photos:
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\BoxInterface;
class PhotoRepository extends ServiceEntityRepository
{
protected $imagineInterface;
protected $mode;
protected $box;
public function __construct(ImagineInterface $imagineInterface,
BoxInterface $box,
$mode = ImageInterface::THUMBNAIL_OUTBOUND)
{
$this->imagineInterface = $imagineInterface;
$this->$box = $box;
$this->mode = $mode;
}
I am getting the typical Cannot autowire service "App\Repository\PhotoRepository": argument "$box" of method "__construct()" references interface "Imagine\Image\BoxInterface" but no such service exists. Did you create a class that implements this interface?
The Imagine\ImageBox class clearly exists in my vendor folder and implements the BoxInterface, it starts out as follows:
namespace Imagine\Image;
use Imagine\Exception\InvalidArgumentException;
/**
* A box implementation
*/
final class Box implements BoxInterface
{
/**
* #var integer
*/
private $width;
Here is a picture of my folder structure, you can see that this Box class is there and that it implements BoxInterface:
I'm stuck because it says that the service doesn't exist but you can see that it does.
Any help is greatly appreciated.
To reply your question regarding working with interfaces, check this section of the docs: https://symfony.com/doc/current/service_container/autowiring.html#working-with-interfaces
However you're misunderstanding the purpose of services. Imagine's BoxInterface is by no means a service and shouldn't be declared as one. A service is needed only when you need only one instance of it through your whole application.
BoxInterface just describes a coordinates of a picture, therefore there will be as many instances as you need pictures instances.
Just create for example $box = new Imagine\Image\Box(50, 50); when you need a box.
I have a Doctrine-Entity in my Symfony2-Project, which uses a custom Assert/Constraint to check, if a given date value is before and/or after a given date. This looks like the following simplified code:
In my entity class:
/**
* #var \DateTime
*
* #ORM\Column(name="entry_entered_at", type="date", nullable=true)
* #AppBundleAssert\DateRangeConstraint(max = "today")
*/
private $entryEnteredAt;
The relevant snippet of the corresponding DateRangeConstraint-class:
new \DateTime($this->max)
As you can see, I want to check, if a date is before today. The \DateTime-constructor is able to resolve this to a DateTime-object of today. Nice thing, works fine.
The problem
But it turns out, that Symfony2 caches all those Doctrine-annotations, so today is always resolved to the day, the cache was lastly cleared and my constraint produces nice form errors.
As a workaround for now, I clear the cache on a daily basis, but I need a better solution.
The question
So the question is, what would you suggest, how to implement such a dynamic assert/constraint in Symfony2?
I could implement the constraint inside the form, but it should be in the domain of the entity.
Edit:
I posted as answer and marked it as solution.
The solution and some answers
It turned out, that the built in Range validator is also able to validate a date-range. So I don't need my custom validator at all.
Digging a bit deeper into the built in Range constraint and the base Constraint class gives the reason, why the built in validators can use dynamic parameters like today, but not my incorrect implemented custom validator. The Constraint base class has a __sleep() method that just stores the object vars and its current values on serialization. Thus, when we don't reinitialize the object with a custom __wakeup() method, which would be a false workaround, we only get the cached parameters.
So besides the fact, that the builtin Range constraint already solves my problem, I simply should have done my dynamic new \DateTime($constraint->max) stuff inside the custom DateRangeConstraintValidator and not the cached custom DateRangeConstraint. Just have a look into Symfony\Component\Validator\Constraints\Range and Symfony\Component\Validator\Constraints\RangeValidator to see this in action.
Lessons learned
Your custom Constraint class will be serialized and cached and thus shouldn't do any dynamic things. Just validate the options and define the messages and stuff. Your dynamic validation things (and especially the initialization of dynamic parameters) must be done within your custom ConstraintValidator class.
I suggest you to look at Custom validator, especially Class Constraint Validator.
I won't copy paste the whole code, just the parts which you will have to change.
Extends the Constraint class.
src/Acme/DemoBundle/Validator/Constraints/CheckEntryEnteredAt.php
<?php
namespace Acme\DemoBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class CheckEntryEnteredAt extends Constraint
{
public $message = 'Your error message.';
public function validatedBy()
{
return 'CheckEntryEnteredAtValidator';
}
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}
Define the validator by extending the ConstraintValidator class, entryEnteredAt is the field you want to check:
src/Acme/DemoBundle/Validator/Constraints/CheckEntryEnteredAtValidator.php
namespace Acme\DemoBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class CheckEntryEnteredAtValidator extends ConstraintValidator
{
public function validate($entity, Constraint $constraint)
{
$today = new \Datetime('today'); // = midnight
if ($entity->entryEnteredAt < $today) {
$this->context->addViolationAt('entryEnteredAt',
$constraint->message, array(), null);
}
}
}
Use the validator:
src/Acme/DemoBundle/Resources/config/validation.yml
Acme\DemoBundle\Entity\AcmeEntity:
constraints:
- Acme\DemoBundle\Validator\Constraints\CheckEntryEnteredAt: ~
(adapted from a previous answer)
public function __construct()
{
$this->entryEnteredAt = new \DateTime();
}
is something like that a solution for your use case? (on new YourEntity() you'll have a today date set for the entryEnteredAt property)
You could also use LifecycleCallbacks, here is an exemple with preUpdate (there is some more, like PrePersist):
on top of your class entity:
* #ORM\HasLifecycleCallbacks()
and
/**
* Set updatedAt
*
* #ORM\PreUpdate
*/
public function setUpdatedAt()
{
$this->updatedAt = new \DateTime();
}
In my application, I have to allow my users to comments two kind of entities : Recipe and News.
I want to know what is the best practice for doing it.
A Comment object with a ref_id(integer) and ref(string) that I manage manually or a commun interface between my to entity and something like #ManyToMany(targetEntoty="MyInterfaceHere") in my Comment object ?
Thanks you for your answers
well a good implementation would be if Recipes and News extend an abstract class
use Doctrine\ORM\Mapping\MappedSuperclass;
/**
* Abstract base class
*
* #MappedSuperclass
*/
abstract class EntityWithComments {
/**
*
*#ORM(many-to-bla)
*/
private $comments;
public function addComment(){...};
public function removeComment(){...};
public function getComments(){...};
...
and your classes extend it, for example Recipe:
class Recipe extends EntityWithComments { ...
so this way you can
$recipe->addComment($comment);
$news->addComment($comment);
straightforward ...
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 1 year ago.
Improve this question
I am working with Symfony2 and Doctrine 2. I am trying to adopt a TDD approach. Can someone give me a basic example of a unit test class for a Doctrine entity class please?
Sincerely appreciate any help.
This is a simple sample of unit test for an entity:
class MessageTest extends \PHPUnit_Framework_TestCase {
/**
* #var Message
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
$this->object = new Message();
}
public function testGetterAndSetter() {
$this->assertNull($this->object->getId());
$date = new \DateTime();
$this->object->setDate($date);
$this->assertEquals($date, $this->object->getDate());
$this->object->setMessage("message");
$this->assertEquals("message", $this->object->getMessage());
$this->object->setSuccess(true);
$this->assertTrue($this->object->getSuccess());
}
}
Does anyone know if it's possible to have a bundle use the annotation reader to read new custom annotations for non Doctrine objects? Everything I've seen so far is either for a controller or to extend Doctrine in some way.
What I'd like to be able to do is something like this:
class MyTestClass {
/**
* #MyBundleName\Foo
*/
public $foo_var;
/**
* #MyBundleName\Bar
*/
public $bar_var;
}
And then have some code that when given an instance of MyTestClass could work out which annotation applied to which attribute.
Right, bit more digging into how Doctrine does this and I think I know how to do it. So if anyone else needs to do this here's how I'm doing it (would be appreciative of any feedback)
I have a service that I'm using to read the annotations so in config.yml I've included the annotation_reader service which provides access to the methods to read your annotations.
Each annotation needs to resolve to a class and the class must extend the base Doctrine annotation class, so to do the Foo annotation from my question you'd do something like:
namespace MyBundleName
class Foo extends \Doctrine\Common\Annotations\Annotation {
}
Then you can read the annotations by doing:
$class = get_class($object);
foreach(object_get_vars($object) as $fieldname => $val){
//$this->annotationReader is an instance of the annotation_reader service
$annotations = $this->annotationReader
->getPropertyAnnotations(
new \ReflectionProperty($class, $fieldName)
);
//$annotations will now contain an array of matched annotations, most likely just an instance of the annotation class created earlier
}
Hope that can be of use to someone else!