Can't get CollectionField to work in EasyAdmin 3.0 - easyadmin

I am trying to use "Tags" in my Account Entity.
So
I have Entity "Account"
I have Entity "Tag"
In "Account" Entity, I have
/**
* #ORM\ManyToMany(targetEntity=Tag::class, inversedBy="accounts")
* #ORM\JoinTable(name="account_tag")
*/
private $tags;
In "Tag" entity I have
/**
* #ORM\ManyToMany(targetEntity=Account::class, mappedBy="tags")
*/
private $accounts;
In my AccountCrudController => ConfigureFields, I use "CollectionField" for my "tags" property
public function configureFields(string $pageName): iterable
{
return [
TextField::new('name'),
AssociationField::new('owner'),
AssociationField::new('parent'),
CollectionField::new('tags'),
];
}
I am getting below
[Expected value of type "App\Entity\Tag" for association field "App\Entity\Account#$tags", got "string" instead.1

You should be able to use an AssociationField here as well, which would fit your purpose.
AssociationField::new('tags') will allow you to reference existing Tags.
If you wish to create all new Tags together, you could use something like this as there is no way to add Tags on the fly in the AssociationField at the moment.
Have you tried setting your CollectionField like this:
CollectionField::new('tags')
->allowAdd()
->allowDelete()
->setEntryType(TagType::class)
;
The important part is the TagType where you define your own FormType. I am also trying to implement this feature, so if you have a fully working example, let us know!

public function configureFields(string $pageName): iterable
{
return [
TextField::new('name'),
AssociationField::new('owner'),
AssociationField::new('parent'),
CollectionField::new('tags')
->SetEntryType(Tag::class)
];
}

Related

What type of data should be passed to domain events?

I've been struggling with this for a few days now, and I'm still not clear on the correct approach. I've seen many examples online, but each one does it differently. The options I see are:
Pass only primitive values
Pass the complete model
Pass new instances of value objects that refer to changes in the domain/model
Create a specific DTO/object for each event with the data.
This is what I am currently doing, but it doesn't convince me. The example is in PHP, but I think it's perfectly understandable.
MyModel.php
class MyModel {
//...
private MediaId $id;
private Thumbnails $thumbnails;
private File $file;
//...
public function delete(): void
{
$this->record(
new MediaDeleted(
$this->id->asString(),
[
'name' => $this->file->name(),
'thumbnails' => $this->thumbnails->toArray(),
]
)
);
}
}
MediaDeleted.php
final class MediaDeleted extends AbstractDomainEvent
{
public function name(): string
{
return $this->payload()['name'];
}
/**
* #return array<ThumbnailArray>
*/
public function thumbnails(): array
{
return $this->payload()['thumbnails'];
}
}
As you can see, I am passing the ID as a string, the filename as a string, and an array of the Thumbnail value object's properties to the MediaDeleted event.
How do you see it? What type of data is preferable to pass to domain events?
Updated
The answer of #pgorecki has convinced me, so I will put an example to confirm if this way is correct, in order not to change too much.
It would now look like this.
public function delete(): void
{
$this->record(
new MediaDeleted(
$this->id,
new MediaDeletedEventPayload($this->file->copy(), $this->thumbnail->copy())
)
);
}
I'll explain a bit:
The ID of the aggregate is still outside the DTO, because MediaDeleted extends an abstract class that needs the ID parameter, so now the only thing I'm changing is the $payload array for the MediaDeletedEventPayload DTO, to this DTO I'm passing a copy of the value objects related to the change in the domain, in this way I'm passing objects in a reliable way and not having strange behaviours if I pass the same instance.
What do you think about it?
A domain event is simply a data-holding structure or class (DTO), with all the information related to what just happened in the domain, and no logic. So I'd say Create a specific DTO/object for each event with the data. is the best choice. Why don't you start with the less is more approach? - think about the consumers of the event, and what data might they need.
Also, being able to serialize and deserialize the event objects is a good practice, since you could want to send them via a message broker (although this relates more to integration events than domain events).

Association is in database, but can't be retrieved via DAL. Will retrieve empty array of associations

I am following the advanced developer tutorial (https://docs.shopware.com/en/shopware-platform-dev-en/how-to/indepth-guide-bundle).
Currently I'm at step 7, and according to the tutorial what I've made so far should work.
But it doesn't.
In the database it shows the association, but I can't retrieve them from the repository.
You have to add the association to the Criteria.
$criteria->addAssociation("name_of_association")
Without it, the associations come as null.
Okay, turns out I switched up two parameters by accident. When I set them correctly it worked as it should.
<?php declare(strict_types=1);
namespace Swag\BundleExample\Core\Content\Product;
use Shopware\Core\Content\Product\ProductDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\EntityExtension;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Inherited;
use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyAssociationField;
use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection;
use Swag\BundleExample\Core\Content\Bundle\Aggregate\BundleProduct\BundleProductDefinition;
use Swag\BundleExample\Core\Content\Bundle\BundleDefinition;
class ProductExtension extends EntityExtension
{
public function extendFields(FieldCollection $collection): void
{
$collection->add(
(new ManyToManyAssociationField(
'bundles',
BundleDefinition::class,
BundleProductDefinition::class,
'product_id',
'bundle_id'
))->addFlags(new Inherited())
);
}
public function getDefinitionClass(): string
{
return ProductDefinition::class;
}
}
I'm talking about the 'product_id' and 'bundle_id'. In my case I had the 'product_id' as the last parameter.

How to hardcode the entity varchar value?

The requirement is to store the hardcoded value for varchar which is in an entity file(.eti). I tried adding to the default option but it is not reflecting.
Default option works well with boolean values (true/false), typelists (you can choose a default typecode), monetary amounts too, but it looks like it is not allowed to specify a default varchar.
Therefore the easiest way would be to create a preupdate rule which inserts that default value every time when you create a new record in the database.
Preupdate rule example:
#gw.rules.RuleName("YourEntityAssignDefaultValue")
internal class YourEntityAssignDefaultValueRule {
static function doCondition(yourEntity : entity.YourEntity) : boolean {
return yourEntity.New
}
static function doAction(yourEntity : entity.YourEntity, actions : gw.rules.Action) {
yourEntity.yourColumn = "defaultValue"
}
}
you can achieve through getter and setter properties in an appropriate enhancement class.
public property get PolicyNumber(): String {
return this.PolicyNumber }
and somewhere class you must be assigned the value to the PolicyNumber field then it will reflect.

Find a div in a block render in Symfony

I am doing a site with symfony 4. I start on this framework and I have a problem of design on my site.
I've been looking for where it might come from, but I can not find the code I'd like to remove.
The part of the code I would like to remove is the following because the result is pretty ugly:
I can not find this in my code:
in my controller :
/**
* #return Response
*/
public function newLetterAction(Request $request): Response
{
$form = $this->createForm(CustomerNewsletterType::class, new Customer());
$form->handleRequest($request);
$facebook = $this->manager->getRepository(ExternalUrl::class)->findOneByCode('facebook');
$instagram = $this->manager->getRepository(ExternalUrl::class)->findOneByCode('instagram');
return $this->templatingEngine->renderResponse('#SyliusShop/Homepage/_newsletter.html.twig', [
'facebook' => $facebook,
'instagram' => $instagram,
'form' => $form->createView(),
'rova_refonte' => (in_array($this->container->get('request_stack')->getMasterRequest()->attributes->get('_route'),["sylius_shop_homepage"]) ? true : false)
]);
}
in my formType :
class CustomerNewsletterType extends AbstractResourceType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class, [
'label' => 'app.ui.newsletter',
'attr' => [
'placeholder' => 'app.ui.email'
]
])
;
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix(): string
{
return 'app_customer_newsletter';
}
}
in my twig:
{{ render(controller('app.controller.shop_homepage:newLetterAction')) }}
if anyone could tell me how to find the code, it would help me a lot.
Thank you
Everything is done under the hood when you call $form->createView().
To sum up, every type of field into the form has a base rendering using twig blocks (so does the form itself), which can be overriden. This is what is called the form theme, there's a base one which is usually this one in the twig-bridge.
You can create new themes, extend the existing one, or event create just what you need for a specific form (hint : the getBlockPrefix function in your form type is used for this).
You can find all the documentation about the form rendering here :
https://symfony.com/doc/current/form/form_customization.html
Most functions described in this documentation are in fact calling twig blocks of form themes, and you can find the documentation about this here :
https://symfony.com/doc/current/form/form_themes.html
Keep in mind: Removing such a class / div might break existing CSS, error rendering or everything done in javascript targeting this class.
Most likely you use a bootstrap 3/4 form theme and there works standard form_row template layout.
To customize current form/another form elements, use "How to Work with Form Themes" tutorial.

Groovy - how to use dynamically domain classes static functions

I have list of domain objects which each one of them need to be called as follows:
(<DOMAIN CLASS>.withCriteria {
dataSecurityGroups {
'in' 'id', entitiesIds as Long[]
}
})
The idea is to have this code once, while changing the code a given parameter.
I know that there are several ways to implement it using groovy, and I tried to use them all.
I need to know what is the best practice and short way to do this.
Thanks!
You said you have a List of domain classes so the code below assumes that is true. You don't say what you want to do with the results of each of those queries, so I will assume you have that under control.
You could do something like this...
def listOfDomainClasses = // you have initialized this list somehow...
listOfDomainClasses.each { domainClass ->
def resultForThisClass = domainClass.withCriteria {
dataSecurityGroups {
'in' 'id', entitiesIds as Long[]
}
})
// do something with resultForThisClass
}
I hope that helps.
I'm assuming you are using Grails, since you tagged this question with Gorm. If so, try this:
Class clazz = grailsApplication.domainClasses.find { it.clazz.simpleName == "<DOMAINCLASS>" }.clazz
clazz.withCriteria {
dataSecurityGroups {
'in' 'id', entitiesIds as Long[]
}
}
Or replace grailsApplication.domainClasses and use your list of domain classes instead.
It is not clear what you are really trying to do but maybe what you want is to write a method like this...
/**
* #param someDomainClass A domain class
* #return the results of the query
*/
def myQueryMethod(Class someDomainClass) {
someDomainClass.withCriteria {
dataSecurityGroups {
'in' 'id', entitiesIds as Long[]
}
}
}
Then you can call that method and pass as an argument whatever domain class is appropriate.
Is that the sort of thing you are looking for?

Resources