I'm new in Sylius and trying to extend it for my business. I'm currently facing an issue on admin part that I don't know how to solve.
I've an Basket entity which is One-To-One related with the Sylius Product entity:
/**
* #ORM\Entity
* #ORM\Table(name="app_basket")
*/
class Basket implements ResourceInterface, BasketInterface
{
/**
* #ORM\OneToOne(targetEntity="App\Entity\Product\Product", inversedBy="basket", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $product;
}
I created this relation to be able to deal with Order Management of Sylius much easier. I don't want to mix Basket and Product objects in my business, so I created a Doctrine filter to exclude Basket items from Products:
<?php
namespace App\Doctrine\Filter;
use App\Entity\Product\Product;
use Doctrine\ORM\Mapping\ClassMetadata;
use \Doctrine\ORM\Query\Filter\SQLFilter;
class ProductFilter extends SQLFilter
{
/**
* #inheritDoc
*/
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if($targetEntity->getName() !== Product::class) {
return '';
}
return "$targetTableAlias.id not in (select product_id from app_basket)";
}
}
On the admin products page, everything is great, I don't have the baskets. But, if I go the the Basket admin index page, I have the following error:
An exception has been thrown during the rendering of a template
("Entity of type 'App\Entity\Product\Product' for IDs id(4) was not
found").
This is because of the grid I use. I want to display the name of the product included in the basket:
sylius_grid:
grids:
app_admin_basket:
driver:
name: doctrine/orm
options:
class: App\Entity\Basket\Basket
fields:
id:
type: string
label: sylius.ui.id
product.name:
type: string
label: sylius.ui.name
To retrieve the basket.product.name, the generated query is querying directly to the product table instead of the basket one:
SELECT t0.code AS code_1, t0.created_at AS created_at_2, t0.updated_at AS updated_at_3, t0.enabled AS enabled_4, t0.id AS id_5, t0.variant_selection_method AS variant_selection_method_6, t0.average_rating AS average_rating_7, t0.main_taxon_id AS main_taxon_id_8, t9.id AS id_10, t9.product_id AS product_id_11
--problem here
FROM sylius_product t0
LEFT JOIN app_basket t9 ON t9.product_id = t0.id
WHERE t0.id = 4
--Doctrine SQL FIlter
AND ((t0.id not in (select product_id from app_basket)));
I've also the same behavior if a get this in a twig template :
{{ basket.product.name }}
With a fetch="EAGER" annotation I don't have the error but the targeted entity still not reflect what I want.
Is there a way to force Sylius Resource to pass through basket entity first and not directly to the embedded entity?
Try this
sylius_grid:
grids:
app_admin_basket:
driver:
name: doctrine/orm
options:
class: App\Entity\Basket\Basket
fields:
id:
type: string
label: sylius.ui.id
product.name:
type: twig
label: sylius.ui.name
path: .
options:
template: thetemplate.html.twig
The path: . property gives you access to the whole entity instead of a specific property. You should then be able to access it using data.product.name in the twig template.
You could also try this, altho I haven't tested it:
sylius_grid:
grids:
app_admin_basket:
driver:
name: doctrine/orm
options:
class: App\Entity\Basket\Basket
fields:
id:
type: string
label: sylius.ui.id
product.name:
type: string
label: sylius.ui.name
path: product.name
Your filter will always prevent fetching the basket's product.
You can try to disable it at least for this request: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/filters.html#disabling-enabling-filters-and-setting-parameters
Related
I'm currently working with the 2.3 version of the easy-admin bundle in Symfony 4.
I try to create a virtual property for the new view.
I have the following configuration
#config/packages/easy_admin.yaml
easy_admin:
entities:
Field:
class: App\Entity\Field
form:
fields:
- { type: tab, label: initial information, icon: pencil-alt }
- name
new:
fields:
- { property: toto, type: file }
and my entity file:
//src/Entity/Field.php
/**
* #ORM\Entity(repositoryClass="App\Repository\FieldRepository")
*/
class Field
{
public function setToto(?File $file): self
{
$this->setImage(new Image);
$this->getImage()->setImageFile($file);
}
as explain in the documentation the setter should be sufficient.
but when I reach the new page I get the following error:
Neither the property "toto" nor one of the methods "getToto()", "toto()", "isToto()", "hasToto()", "__get()" exist and have public access in class "App\Entity\Field".
which means that the page is looking for getter and not setter. Is it normal or did I make something wrong ?
I have just ran into this issue and I have solved it by adding the getter.
As you said, it is looking for getter but also setter.
I'm struggling with an issue based on the use case that is described here :
Use-Case 2: Simple Derived Identity
I have the following Doctrine entities and mapping in my Symfony app:
class User
{
private $entity_id;
private $address;
...
}
class Address
{
private $user;
...
}
AppBundle\Entity\User:
type: entity
id:
entity_id:
type: integer
generator:
strategy: AUTO
oneToOne:
address:
targetEntity: Address
mappedBy: user
cascade: ["persist"]
AppBundle\Entity\Address:
type: entity
id:
user:
associationKey: true
oneToOne:
user:
targetEntity: User
inversedBy: address
joinColumn:
name: entity_id
referencedColumnName: entity_id
Every time I perform a DQL query that involves the User entity, Doctrine performs one additional query per matching User to retrieve the corresponding Address entity. That happen every time, even if the Address data are never used in the code.
I tried to reproduce this issue on a vanilla Symfony installation, and I faced another issue, I'm not able to perform the following code as I get an error (Entity of type AppBundle\Entity\Address is missing an assigned ID for field 'user'):
$user = (new User())->setAddress(new Address());
$entityManager->persist($user);
$entityManager->flush();
Do you have any hint or what is wrong?
Best regards
I have setup a database table in which the ID values will be created by my application and NOT the database.
/**
* #ORM\Column(type="bigint", precision=14, options={"unsigned":true})
* #ORM\Id()
* #ORM\GeneratedValue("NONE")
*/
private $id;
This works fine in symfony, but I am trying to edit the table using EasyAdmin and EasyAdmin simply omits the 'id' column.
I found out that I can manipulate edit/new views configuration from EasyAdmin documentation.
Now I have the following configuration (the mentioned id is for Product):
easy_admin:
entities:
- AppBundle\Entity\Category
- AppBundle\Entity\Product
Question:
1- How do I setup the YAML configuration so id field will also appear? I found out that this partially works:
easy_admin:
entities:
Product:
class: AppBundle\Entity\Product
form:
fields:
- 'id'
But this shows only 'id', is there a way to tell that I want 'id' in addition to all the other fields so I don't have to list them manually?
2- My original config is using a list of entities with dash (-) in the YAML file. I am a YAML noob, when I make a Product: key I am not able to use the dash anymore, is there a way to keep using dash list and just make an exception for 'Product? For example the code below does NOT work, it says it is not valid YAML.
easy_admin:
entities:
- AppBundle\Entity\Category
Product:
class: AppBundle\Entity\Product
form:
fields:
- 'id'
Well, for now I solved the problem like this and abandoned the dash notation altogether:
easy_admin:
entities:
Category:
class: AppBundle\Entity\Category
Store:
class: AppBundle\Entity\Store
Product:
class: AppBundle\Entity\Product
edit:
fields:
- { property: 'stores', label: 'Stores', type_options: { by_reference: false } }
form:
fields:
- 'id'
- 'name'
- 'category'
- 'stores'
Q1: you can use customization based on entity controllers. See doc here: https://symfony.com/doc/master/bundles/EasyAdminBundle/book/complex-dynamic-backends.html#customization-based-on-entity-controllers
app/config/config.yml
User:
class: AppBundle\Entity\User
controller: UserBundle\Controller\Admin\UserController
And then in your UserController you can have something like this. Pay attention you must use the exact entity name in method signature: createUserEntityFormBuilder in your case
protected function createUserEntityFormBuilder($entity, $view)
{
$form = parent::createEntityFormBuilder($entity, $view);
$form->add('Anyfield', TextType::class, [
'label' => 'id' // feel free to add other options
]); // add fieldlike you would do in FormType
$form->remove('anyField');
return $form;
}
Q2: I can't answer to this question for sure. I do not use "Dashed" notation.
Maybe take a look at doc here: https://symfony.com/doc/current/components/yaml/yaml_format.html#collections
I have got logging working on my entity so that when I make a change to a product field with the #Gedmo\Versioned annotation a new version is created. However the only problem is that the username field remains NULL. There is an authenticated user as the update is performed in Sonata Admin backend.
<?php
namespace MyApp\CmsBundle\Entity\Log;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Loggable\Entity\MappedSuperclass\AbstractLogEntry;
use Gedmo\Loggable\Entity\Repository\LogEntryRepository;
/**
* #ORM\Entity(repositoryClass="Gedmo\Loggable\Entity\Repository\LogEntryRepository", readOnly=true)
* #ORM\Table(
* name="log_product",
* indexes={
* #ORM\Index(name="log_class_lookup_idx", columns={"object_class"}),
* #ORM\Index(name="log_date_lookup_idx", columns={"logged_at"}),
* #ORM\Index(name="log_user_lookup_idx", columns={"username"}),
* }
* )
*/
class ProductLog extends AbstractLogEntry
{
}
So it would appear the log_user_lookup_idx isn't working correctly, is there a particular bit of config I require for this?
It appears I was missing a bit of config, adding the following to the main app/config/config.yml file did the trick.
stof_doctrine_extensions:
default_locale: en
orm:
default:
loggable: true
I did originally have this in my bundles' services.yml config:
gedmo.listener.loggable:
class: Gedmo\Loggable\LoggableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ "#annotation_reader" ] ]
This managed to track the entity being modified but not the user, I have since removed this config and the logging remains to work with just the stof_doctrine_extensions config setting.
If you have both in your code base then everything will be logged twice I found.
I am using Symfony 2.4 and doctrine ORM. I have a parent entity, Property, which has many child relations including:
propertyVideos (OneToMany)
propertyPhotos (OneToMany)
propertyLocation (OneToOne)
In the lastUpdated field of the Property entity, I need to store the date and time of the last update of the Property entity or any of its related entities.
Is there an easy way to do this in Symfony/Doctrine?
Easiest bet would be to use the gedmo/doctrine-extensions or (for Symfony) stof/doctrine-extensions-bundle which you can then use the do the following...
Entity\Article:
type: entity
table: articles
fields:
created:
type: date
gedmo:
timestampable:
on: create // $this create
updated:
type: datetime
gedmo:
timestampable:
on: update // $this update
published:
type: datetime
gedmo:
timestmpable:
on: change
field: type.title
value: Published
// $this->type->title changed to "Published"
blah:
type: datetime
gedmo:
timestampable:
on: change
field: type.somethingelse
// $this->type.somethingelse is changed
manyToOne:
type:
targetEntity: Entity\Type
inversedBy: articles