Expected path to Sluggable builder in Symfony/Doctrine - php

The default string normalization (provided by the Doctrine_Inflector::urlize() "sluggifier") isn't suitable for my project, so I need to utilize my own algorithm.
I set my schema up as follows:
MyObject:
actAs:
Sluggable:
fields: [name]
builder: array('TextUtility', 'normalize')
columns:
name: string(255)
And I added my utility class to the lib folder of my project (although I also tried the lib folder of an app) according to some instructions I found in another forum:
<?php
//lib/TextUtility.class.php
class TextUtility {
public static function normalize($str) {
/* ... */
return $str;
}
}
?>
When I run symfony doctrine:build --all I'm greeted by the following error:
Warning: call_user_func_array() expects parameter 1 to be a valid callback, function 'array('TextUtility', 'normalize')' not found or invalid function name in /symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine/Template/Listener/Sluggable.php on line 171
I assume I'm just putting my utility in the wrong place and it's not being loaded when needed. Am I putting it in the wrong place? Doctrine's documentation doesn't seem to mention the subject, unless I'm just looking at the wrong page.

arrays in YAML are defined other way:
MyObject:
actAs:
Sluggable:
builder: [TextUtility, normalize]

Related

Is it possible to load Swagger annotations from a different class or file?

I have the following simple PHP method like the following
/**
*
* (swagger annotation to be called from a different class)
*
*/
public function getApiCall()
{
//Do something
}
and I need to include long Swagger documentation into the annotation above the method, so
is it possible to write the annotation in a different class ? and call it here with something like
/**
*
*call('App\Http\Controllers\testAnnotation');
*/
The main purpose is to have a clean class without so many lines of documentation and annotations in it.
Loading "annotations from a different class" is not something that makes a lot of sense. Annotations are read in the annotated code, that's their whole purpose.
But if you want to keep configuration and code separated, you do not have to use Swagger-Php to generate your swagger configuration file.
The package is simply a convenience way to generate the swagger.json file from code annotations.
But if you do not want to use annotations in the first place, and keep your classes clean from extraneous configuration (something that I personally applaud), just... do not use Swagger-Php and build your own configuration files outside of your classes.
You could even write it in YAML, if you feel more comfortable than writing JSON by hand. For example::
openapi: 3.0.0
info:
title: 'Search API'
version: 1.0.0
servers:
- url:
description: Current host server
- url: https:your-server.com
description: Prod server
paths:
/foo:
post:
summary: 'Creates a new foo'
description: 'Builds a new Foo and makes it available to Bar'
requestBody:
description: 'Foo '
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
responses:
'201':
description: Foo created
'202':
description: Foo queued, it will be eventually created.
components:
schemas:
Foo:
type: object
required:
- name
- size
properties:
name:
type: string
size:
type: integer
This, once converted to JSON (there are many libraries to do this, or you could even use a free service like this one), you can feed the resulting JSON to swagger directly.
E.g. the YAML above parses to this JSON file. You can easily test it out by heading to the Swagger demo instance, and past the JSON URL in the "explore" location bar, and you'll get something like this:
In the end, it's not much more work than using annotations (if any more work at all), and you can keep your entity classes clean from configuration concerns.

Why/how does this work: Symfony 2.8 + FOSRestBundle automatically deserializes request object

I am working on migrating an existing Symfony 2.8 which I did not create. The previous developers are not available for questions.
Most of the code easy to understand, but at one point I simply cannot figure out how are why this works:
A controller/action parameter is automatically de-serialized from JSON data to a custom object. This is nothing special, I do not understand how/where the Symfony is told what to do. Form my point of view important config data is missing but it works anyway.
That's would be fine, but without understanding why it works in this case, I cannot figure out why it does not work anymore when migrating the project to Symfony 3.4...
Sorry for the (very) long question, but I tried different ways to solve the problem and answer the questions but they all resulted in different problems...
Using the following Bundles
sensio/framework-extra-bundle v3.0.29
friendsofsymfony/rest-bundle 2.4.0
jms/serializer 1.13.0
jms/serializer-bundle 1.5.0
Config:
// app/config/config.yml
sensio_framework_extra:
request: { converters: true }
fos_rest:
...
body_converter:
enabled: true
#jms_serializer: (not configured)
# ...
Code:
// src/AppBundle/Util/SortInfo.php
class SortInfo {
public $sortOrder;
public $sortBy;
}
// src/AppBundle/Resources/serializer/Util.SortInfo.yml
AppBundle\Util\SortInfo:
exclusion_policy: ALL
properties:
sortOrder:
type: string
expose: true
sortBy:
type: string
expose: true
// src/AppBundle/Controller/SortingController.php
class SortingController extends Controller {
...
/**
* #FOSRest\View()
*/
public function sortAction(SortInfo $sortInfo) {
$this->logger->info("sortAction");
$this->logger->info(" ".gettype($sortInfo)." -- ".get_class($sortInfo));
$this->logger->info(" ".$sortInfo->sortOrder." -- ".$sortInfo->sortBy);
...
}
}
That's it. After digging for some hours I could not find any explicit configuration that would tell the sortAction method to convert the JSON data from the request automatically. But it works.
A request with the following JSON content results in the following log output:
// JSON content
{"sortOrder":"ASC", "sortBy":"name"}
// Log output
sortAction
object -- AppBundle\Util\SortInfo
ASC -- name
Why does this work?
As said before this is fine, but the problem is, that the same code does not work with Symfony 3.4 and newer versions of FOSRest, ExtrasBundle and JMSSerializer:
sensio/framework-extra-bundle v5.2.4
friendsofsymfony/rest-bundle 2.5.0
jms/serializer 2.1.0
jms/serializer-bundle 3.0.0
A request with the same JSON data results in the following log output in `Symfony 3.4':
// Log output
sortAction
object -- AppBundle\Util\SortInfo
--
So although the SortInfo is created (not null), the sortBy and sortOrder properties are empty/null.
How can this be? I do not understand why the data is de-serialized in the first place, but how it is possible to create a SortInfo object without setting the properties is even more mysterious.
In Symfony 2.8 the Util.SortInfo.yml file is necessary for the process. Removing this files results in an exception:
Symfony\Component\HttpKernel\Exception\BadRequestHttpException: "You
must define a type for
AppBundle\Util\SortInfo::$sortBy."
at /.../vendor/friendsofsymfony/rest-bundle/Request/RequestBodyParamConverter.php
Here I do not understand why SortInfo::$sortBy is a problem but not SortInfo it self. Additionally removing the file in Symfony 3.4 does not change anything.
But at least this error message confirms, that the FOS RequestBodyParamConverter is involved in the process.
Following the Symfony docs it should be necessary to manually specify that the parameter that should be converted (although it works just fine without this config in Symfony 2.8):
/**
* #FOSRest\View()
* #ParamConverter("sortInfo", converter="fos_rest.request_body")
*/
public function sortAction(SortInfo $sortInfo) {
...
}
However, adding this config results in another error in Symfony 3.4:
Uncaught PHP Exception RuntimeException: "Converter
'fos_rest.request_body' does not support conversion of parameter
'$sortInfo'
Bottom line:
This is all very mysterious.
Why does it work without any problem in Symfony 2.8 although regarding to the docs essential config is missing (explicitly specify ParamConverter)
Why does it NOT work in Symfony 3.4?

Symfony: Unknown "humanize_bytes" filter

In a Symfony 2.7 app, we have attempted to set up a humanize_bytes Twig filter in order to convert long numbers of bytes into human-readable form -- 10 MB, for example.
Within our HumanReadableBytesExtension.php file is the following:
public function getFilters() {
return [
new TwigFilter('humanize_bytes', [$this, 'getHumanReadableBytesFilter'])
];
}
... and in our services.yml file lies the following:
mycompany.cms.twig.extension.human_readable_bytes_extension:
class: MyCompany\TwigExtensions\HumanReadableBytesExtension
arguments:
- '#translator'
tags:
- {name: twig.extension}
... but we find that the getFilters() method is not getting called, and that when we try to call the filter in a Twig template, we get:
Unknown "humanize_bytes" filter.
Both files pass syntax validation. The cache has been cleared. Is there somewhere else where we should be registering this filter?
====
Edit: Here is the output of the app/console debug:container mycompany.cms.twig.extension.human_readable_bytes_extension command:
[container] Information for service
mycompany.cms.twig.extension.human_readable_bytes_extension Service Id
mycompany.cms.twig.extension.human_readable_bytes_extension Class
MyCompany\TwigExtensions\HumanReadableBytesExtension Tags
- twig.extension () Scope container Public yes Synthetic no Lazy no
Synchronized no Abstract no
You mentioned you are using an abstract class. Did you override the getName method in your HumanReadableBytesExtension ?
If two extensions have the same name, only one will be loaded, the second will be silently ignored.
I ultimately just took all of my changes and put them on a fresh feature branch. That "fixed" the problem, albeit in a very non-satisfactory way. (We never really did figure out what was going wrong.)

Symfony 2 : services dependencies failed

Since yesterday, I can't understand why i have this error on my Symfony website.
I have a service which depends on others. When i try to use and inject the Symfony doctrine entity manager i have errors. I can't find a way to do it :(
My config :
mycompany_jobs_bundle.processor.crm.product_matrix:
class: %mycompany_jobs_bundle.processor.crm.product_matrix.class%
parent: pim_base_connector.processor.product_to_flat_array
arguments:
- '#pim_catalog.repository.attribute'
- '#pim_catalog.localization.factory.date'
- '#doctrine.orm.default_entity_manager'
(I also tried 'doctrine.orm.entity_manager' instead of '#doctrine.orm.default_entity_manager' -> same results)
Then, in my service :
I add a "use Doctrine\ORM\EntityManager;" with the other "use" lines.
And this is my constructor :
... but i have this error :
Catchable Fatal Error: Argument 8 passed to
Mycompany\Bundle\JobsBundle\Processor\Mycompany\ProductWordMatrixProcessor::__construct()
must implement interface
Pim\Component\Catalog\Repository\AttributeRepositoryInterface,
instance of Doctrine\ORM\EntityManager given
Wherever i move my line "EntityManager $em" i have error with arguments position. What's wrong ? I can't understand ..
Thanks for your help
Edit : below the yml config of the parent service, pim_base_connector.processor.product_to_flat_array :
pim_base_connector.processor.product_to_flat_array:
class: %pim_base_connector.processor.product_to_flat_array.class%
arguments:
- '#pim_serializer'
- '#pim_catalog.manager.channel'
- '#pim_catalog.builder.product'
- ['pim_catalog_file', 'pim_catalog_image']
- %pim_catalog.localization.decimal_separators%
- %pim_catalog.localization.date_formats%
- '#akeneo_storage_utils.doctrine.object_detacher'
Try to move the EntityManaer as the last parameter of your constructor arguments like this:
public function __construct()
{
//.. others
AttributeRepositoryInterface $attributeRepository,
EntityManager $em
}
You need to maintain the order of your dependencies declared inside the configuration
You constructor has 10 arguments and all of them should be configured. You specified only 7. You have to specify last 3 as well and check the order. It's important.

Fatal Error with Doctrine while using generate.php

I'm working on the Doctrine tutorial at http://www.Doctrine-project.org/ and I receive a fatal error when I try to run my generate.php script which makes my models and makes tables in the database:
Fatal error: Class 'BaseCharity' not found in ...\models\Charity.php on line 14
generate.php:
require_once('bootstrap.php');
Doctrine_Core::dropDatabases();
Doctrine_Core::createDatabases();
Doctrine_Core::generateModelsFromYaml('schema.yml', 'models');
Doctrine_Core::createTablesFromModels('models');
and schema.yml
Charity:
actAs: [Timestampable]
columns:
active:
type: boolean
default: '1'
owed: decimal(32,2)
totalPayed: decimal(32,2)
name: string(255)
website: string(255)
description: text
icon: string(255)
I am quite stumped by this, I can get it to correctly create other tables that are very similar or much more complicated then this one. I've tried rewriting it as well. I really have no clue where this error is coming from.
You need to register the models with the autoloader provided by Doctrine. No need to use any Iterators or what so ever
Doctrine::loadModels('path/to/your/models');
You can of course use it sevaral times:
Doctrine::loadModels('path/to/your/models/generated');
Doctrine::loadModels('path/to/your/models/custom');
Doctrine::loadModels('path/to/your/models');
Found this:
http://www.doctrine-project.org/jira/browse/DC-344
Hi, I've stumbled upon the same
problem and I think I know where the
issue is.
So
Doctrine_Core::createTablesFromModels()
calls Doctrine_Export::exportSchema()
which in turn calls
Doctrine_Core::loadModels().
Doctrine_Core::loadModels() uses
RecursiveIteratorIterator and iterates
over all found files.
Now I think the order of files
returned by RecursiveIteratorIterator
is not always the same (depends on OS,
filenames and cosmic radiation ), but
the most important thing here is that
class files from 'modules/generated'
directory (as in examples) ARE NOT
included before subclasses derived
from generated classes. This means
that Doctrine_Core::autoload() fails
to load classes from
'modules/generated' directory, exactly
this check fails:
if (0 !== stripos($className,
'Doctrine_') ||
class_exists($className, false) ||
interface_exists($className, false))
as base class is not starting with
'Doctrine_' and is not yet loaded.
To fix it properly the algorithm for
loading modules must be changed to
first include 'modules/generated'
classes and then rest of classes. I am
not sure but maybe Core::autoload()
might be changed to include base
classes properly.
QUICK WORKAROUND: As a quick
workaround I've changed parameters in
call to createTablesFromModels() to:
Doctrine_Core::createTablesFromModels(array('models/generated','models'));
as createTablesFromModels() can accept
array of directories.
Hope this helps you, please let me
know if you need any more information.
Thanks!

Categories