How to override Product model in Sylius? - symfony

I am working on Sylius 1.2.8. I have overridden Product model and it is working fine but when I add a new product with attributes:
<?php
...
$em = $this->container->get('sylius.manager.product');
/**
* #var \Sylius\Component\Product\Model\ProductAttributeValueInterface $attribute
* #var \Sylius\Component\Core\Model\ProductInterface $product
*/
$product->addAttribute($attribute);
$em->persist($product);
$em->flush();
It throws this error:
An exception occurred while executing 'INSERT INTO
sylius_product_attribute_value (locale_code, text_value,
boolean_value, integer_value, float_value, datetime_value, date_value,
json_value, product_id, attribute_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?,
?, ?)' with params ["en_US", null, null, null, null, null, null,
"[\"013ea12a-1aff-4050-8107-20b53ada73ce\"]", null, 28]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'product_id' cannot be null
My custom Product Model looks like this:
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Sylius\Component\Core\Model\Product as BaseProduct;
class Product extends BaseProduct implements ProductInterface
{
public function __construct()
{
parent::__construct();
}
}
And my config file:
sylius_product:
driver: doctrine/orm
resources:
product:
classes:
repository: AppBundle\Doctrine\ORM\ProductRepository
model: AppBundle\Entity\Product
I have found a similar question asked :stackoverflow.com/q/22919004/6248367. But this does not solve my problem neither does it answer why application fails. This answer is also 4 years old. Can someone help me fix this?
Edit: This error is also thrown in admin whenever I create a new product and add attributes to it at the same time. Workaround is to first create product, then add attributes.

I have figured out the problem. As #czende mentioned properly check the configuration.
In my case I had set the wrong class in subject while overriding the attribute:
sylius_product:
driver: doctrine/orm
resources:
product:
classes:
repository: AppBundle\Doctrine\ORM\ProductRepository
model: AppBundle\Entity\Product
sylius_attribute:
driver: doctrine/orm
resources:
product:
# Make sure to provide the correct Product class in the subject.
subject: AppBundle\Entity\Product
attribute:
classes:
model: AppBundle\Entity\ProductAttribute
repository: AppBundle\Doctrine\ORM\ProductAttributeRepository
Also make sure to use mappedSuperclass in the mapping config instead of entity as commented by MichaƂ Marcinkowski in issue #3997
AppBundle\Entity\ProductAttribute:
type: mappedSuperclass
table: sylius_product_attribute
EDIT: To debug what's wrong, start by checking warnings in symfony debug bar. It can narrow down your search area for bug. It can also hint you if something is wrong in your configuration file.

Related

Sylius Resource and Doctrine Filter

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

Gedmo Translatable yaml mapping

Is it possible to map Gedmo with yaml ? I'm using yaml file in my project and I would like to use Translatable from doctrine extensions but I have a problem with it.
I have something like that (one entity / one translation table) :
class CategoryTranslation extends \Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation
{
/**
* All required columns are mapped through inherited superclass
*/
}
but, when I run "doctrine:schema:update --dump-sql" command, I've got an error :
There is no column with name 'locale' on table 'category_translation'.
I have the feeling that yaml mapping "can't see" Gedmo AbstractTranslation class...
Do you have an idea of the problem ?
Thank you !
EDIT :
To avoid discussions in comments, I post an answer.
MyProject\MyBundle\Entity\Category:
type: entity
table: category
repositoryClass: MyProject\MyBundle\Repository\CategoryRepository
gedmo:
tree:
type: nested
translation:
locale: locale
entity: MyProject\MyBundle\Entity\Translation\CategoryTranslation
fields:
label:
type: string
length: 255
gedmo:
- translatable
With this config, I had to create a Translation.CategoryTranslation.orm.yml file because there is no database migration. And, there problem is really here :
MyProject\MyBundle\Entity\Translation\CategoryTranslation:
type: entity
table: category_translation
repositoryClass: Gedmo\Translatable\Entity\Repository\TranslationRepository
indexes:
category_translation_idx:
columns: [ locale, object_class, field, foreign_key ]
id:
id:
type: integer
generator:
strategy: AUTO
fields:
locale:
type: string
length: 8
object_class:
type: string
length: 255
field:
type: string
length: 32
foreign_key:
type: string
length: 64
content:
type: text
By doing this, the database migration works. I have a new table named category_translation. But, when I try to add a new translation, there is an error in my sql request generated by doctrine. Object_class field and foreign_key are null.
Here, the SQL request generated by doctrine :
INSERT INTO category_translation (locale, object_class, field, foreign_key, content) VALUES (?, ?, ?, ?, ?)' with params [\"fr\", null, \"name\", null, \"toto\"]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1048 Column 'object_class' cannot be null"}

Symfony2 Doctrine Migration failed

I'm new to Symfony. In Symfony 2.8.3 project I created mirgation file using
php app/console doctrine:migrations:generate
But when I put some code in up() and down() methods and try to run
php app/console doctrine:migrations:migrate
I get mistake:
Migration 20160314161511 failed during Pre-Checks. Error Notice: Undefined offset: 1
[Symfony\Component\Debug\Exception\ContextErrorException]
Notice: Undefined offset: 1
I tried to put different code by using "clear" SQL or via scheme, even left up/down methods with only //comments. But still no success.
DoctrineMigrationsBundle was installed and registered in AppKernel.php.
Here's the code of my aim mirgation:
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20160314161511 extends AbstractMigration
{
/**
* #param Schema $schema
*/
public function up(Schema $schema)
{
$this->addSql(
"CREATE TABLE tblProductData (
intProductDataId int(10) unsigned NOT NULL AUTO_INCREMENT,
strProductName varchar(50) NOT NULL,
strProductDesc varchar(255) NOT NULL,
strProductCode varchar(10) NOT NULL,
dtmAdded datetime DEFAULT NULL,
dtmDiscontinued datetime DEFAULT NULL,
stmTimestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (intProductDataId),
UNIQUE KEY (strProductCode)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Stores product data'"
);
}
/**
* #param Schema $schema
*/
public function down(Schema $schema)
{
$this->addSql(
'DROP TABLE tblProductData'
);
}
}
Here is the doctrine_migrations section of my config.yml:
doctrine_migrations:
dir_name: "%kernel.root_dir%/DoctrineMigrations"
namespace: Application\Migrations
table_name: migration_versions
name: Application Migrations
doctrine section of config.yml I left with default value
When I use
php app/console doctrine:migrations:status
I get Available Migrations: 1 and New Migrations: 1
This is a known bug in zend-code library which is a dependency of DoctrineMigrations and this issue appears only on Windows. The temporary workaround is to go to vendors/zend-code/Generator/MethodGenerator.php#L96 and change:
protected static function clearBodyIndention($body) {
....
$lines = explode(PHP_EOL, $body);
....
$body = implode(PHP_EOL, $lines);
....
}
to:
protected static function clearBodyIndention($body) {
....
$lines = explode("\n", $body);
....
$body = implode("\n", $lines);
....
}
The developer of DoctrineMigrations is aware of this bug and you can find more info in this issue.
At this point there is a pull request to zend-code and hopefully this will be merged soon into the master which will then fix the problem.

Why my file-update Listener don't work like variant_image on sylius project?

I use a standalone version of SyliusResourceBundle (0.9) in my project.
I can manage without any problem my entities: User, Group, Role and Company.
I tried using Gaufrette\Filesystem to add a logo to a company (like variant_image in sylius).
And it seems that my service is not running. He tries to persist the image without it have been uploaded and I have no error message from my listener !
KNP Gaufrette Configuration in config.yml :
knp_gaufrette:
adapters:
project_file:
local:
directory: %kernel.root_dir%/../web/media/file
create: true
filesystems:
project_file:
adapter: project_file
ProjectCoreBundle\Resources\Config\services.yml :
#in ProjectCoreBundle\Resources\Config\services.yml
# Listener
project.listener.image_upload:
class: %project.listener.image_upload.class%
arguments: ['#project.image_uploader']
tag:
- { name: kernel.event_listener, event: project.company.pre_create, method: uploadCompanyLogo }
- { name: kernel.event_listener, event: project.company.pre_update, method: uploadCompanyLogo }
# Other
project.image_uploader:
class: %project.image_uploader.class%
factory_class: Gaufrette\Filesystem
factory_service: knp_gaufrette.filesystem_map
factory_method: get
arguments: [project_file]
My ImageUploadListener :
<?php
namespace Project\CoreBundle\EventListener;
use Project\CompanyBundle\Entity\CompanyInterface;
use Project\CoreBundle\Uploader\ImageUploaderInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
class ImageUploadListener
{
protected $uploader;
public function __construct(ImageUploaderInterface $uploader)
{
$this->uploader = $uploader;
}
public function uploadCompanyLogo(GenericEvent $event)
{
$subject = $event->getSubject();
if (!$subject instanceof CompanyInterface) {
throw new UnexpectedTypeException(
$subject,
'Project\CompanyBundle\CompanyInterface');
}
$logo = $subject->getLogo();
if ($logo->hasFile()) {
$this->uploader->upload($logo);
}
}
}
When i send my form with an image, i got this message :
An exception occurred while executing 'INSERT INTO syn_image (path, createdAt, updatedAt) VALUES (?, ?, ?)' with params [null, "2014-05-23 18:13:13", "2014-05-23 18:13:13"]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'path' cannot be null
I feel that my service does not fire, Anyone can help me with this problem ?
Thanks :)
It seems like the image uploader feature is still being developed. I've noticed that the image uploader doesn't work when there's already an image that's part of the collection as did the person who created this github issue: issue #356. There's discussion on what could be done/what tools could be used to develop a better uploader here: link.

symfony hateoas and jms serialization

I have an entity Customer that i want to have both Hatoas Links and custom serialization
/**
* Customer ORM Entity
*
* #package AppBundle\Entity
*
* #Hateoas\Relation("self", href = #Hateoas\Route("get_customers", parameters = { "customer" = "expr(object.getId())" }))
* #Hateoas\Relation("customers", href = #Hateoas\Route("cget_customers"))))
*/
That is the annotation for the hateoas links
AppBundle\Entity\Customer:
exclusion_policy: ALL
virtual_properties:
getFullName:
serialized_name: full_name
type: string
That is my yaml configuration for the jms serialization but for some reason it also remove the hateoas links.
How can i get it back? Tell to the serializer to not remove the _links property?
In Hateoas documentation it says:
Important: you must configure both the Serializer and Hateoas the same way. E.g. if you use YAML for configuring Serializer, use YAML for configuring Hateoas.
For example, use the YAML format for configuration and your issue should be solved.
As #takeit say, you should use the same configuration used with the serializer. For your example try this:
AppBundle\Entity\Customer:
exclusion_policy: ALL
virtual_properties:
getFullName:
serialized_name: full_name
type: string
relations:
-
href:
route: get_customers
parameters:
customer: expr(object.getId())
# absolute: 0
customers:
route: cget_customers
Hope this help

Resources