Code Monkey home page Code Monkey logo

normalizerbundle's Introduction

Bowl Of Soup Normalizer

Build Status codecov PHP Version Symfony Version

Installation

composer require bowlofsoup/normalizer-bundle

Add the bundle to your config/bundles.php file

BowlOfSoup\NormalizerBundle\BowlOfSoupNormalizerBundle::class => ['all' => true],

Quick feature overview

  • It's a Symfony bundle!
  • Normalizes class properties and methods (public, protected, private)
  • Can Serialize normalized content
  • Works with Symfony and Doctrine as its ORM. Can handle Doctrine proxies
  • Circular reference check: Handles circular reference by detecting it and returning content of the objects getId() method
  • Object caching: If a getId() method is implemented for an object it will cache the normalized object per normalize command
  • Annotation caching, this means speed!
    • The annotations for an object are cached. This means not parsing annotations multiple times for the same object. per flow (per normalize command)
    • In Symfony prod mode, annotations are cached completely (after first run)
  • Symfony translations
    • Indicate domain (translation filename) and locale in annotations
    • Does not support formatting with ICU MessageFormat (yet), so no parameters

The main features are described in the documentation.

Documentation

Documentation on the usage and all supported options can be found in the wiki.

  1. What is serialization and normalization?
  2. Installation
  3. Serializing
    1. Serialize annotations
  4. Normalizing
    1. Normalize annotations
  5. Translate a value
    1. Translate annotations

Why use this normalizer and not ...

  • The Bowl Of Soup Normalizer uses an opt-in mechanism by default. You have to indicate which properties must be normalized
  • You can indicate a context group, how is the value to be normalized, in which context?
  • It's designed with speed in mind. Not packed with features for which you don't use half of it
  • It has proven itself in a complex application with 15.000+ daily end users

Development

The following CI tools can be used to check for code quality before pushing code:

Rector

Rector can be used to automated code upgrades and refactoring. Try a dry-run first!

vendor/bin/rector process --dry-run --no-progress-bar --ansi

PHPStan

PHPStan is a static code analysis tool that focuses on finding errors in the code. Fixing the outcome of PHPStan prevents possible bugs and errors.

vendor/bin/phpstan

PHPUnit

Speaks for itself, code should be tested. Run with coverage (output = tests/coverage):

XDEBUG_MODE=coverage php -dzend_extension=xdebug.so vendor/bin/phpunit 

Or without coverage:

vendor/bin/phpunit

Code coverage master:

Code style fixer

Have php-cs-fixer automatically fix styling.

vendor/bin/php-cs-fixer fix

normalizerbundle's People

Contributors

bowlofsoup avatar gabriel-255 avatar raymondschouten avatar wcoppens avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

normalizerbundle's Issues

Can't use callback together with type="datetime"

I have a callback method that returns a DateTime object, which I'd like to normalize to specific formats on different places. Therefore I'd like to use:

@Bos\Normalize(type="datetime", format="Y-m-d", callback="getCalculatedDate")

But this completely ignores the callback method.

In my opinion the callback method must only act as an alternative source for the property data and not influence any other functionality.

Normalize all class properties, without an opt-in

Currently this normalizer works with an opt-in mechanism, that means you have to specifically indicate which properties or methods are normalized (with optional use of group).

It should be possible to indicate on class level (with optional group) to just normalize all properties in that class.

Support PHP 8.x + add CI tools

  • Support for PHP 8.x should be added (should check up until which Symfony version can be used)
  • Rector should be added to help refactoring and upgrade.
  • Update bundle to use newer Symfony 4.x/5.x bundle structure
  • Aim should be to keep versioning of this bundle in line with Symfony

(Investigation) More information on the bundles:

Exactly which properties/methods are normalized?

Make analysis on which methods and properties: public, protected and private are normalized?
How many parent classes deep does it find methods and properties?

  • I would expect all properties, (public, protected and private) will be normalized, from any parent class.
  • I would expect all methods (public, protected and private) will be normalized, from any parent class.

Add maximal depth

Adding maximal depth functionality on class and class property level.

Improve performance by caching annotations in production-mode

Performance in production-mode can be greatly improved by caching the annotations between requests, just like the @route annotations in Symfony, as in production-mode the annotations are not expected to change. The annotations can be cached during cache:warmup in the Symfony cache folder.

Use filesystem cache for annotations

Currently Symfony uses ArrayCache by default. We should write the annotations to the FileSystem cache instead. This should increase speed significantly.

Normalizing Doctrine proxy, ID property is reflected to a string.

When normalizing a doctrine proxy, the ID property of that object is reflected to a string. If the object is initialized, and not a proxy anymore, the value is (correctly) an integer.

Possible solution: If object doctrine proxy, use getId() instead of property reflection.

Parent property/method value should not overwrite child value

I have an abstract:

abstract class AbstractPerson
{
    /**
     * @var string
     *
     * @Bos\Normalize(group={"parent_test"})
     */
    private $name = 'parent-foo';
}

I have a class that extends this abstract:

class Person extends AbstractPerson
{
    /**
     * @var string
     *
     * @Bos\Normalize(group={"parent_test"})
     */
    private $name;

    /**
     * @return $this
     */
    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }
}

When executing this unit test:

$person = (new Person())->setName('child-foo');

$this->assertSame(['name' => 'child-foo'], $this->normalizer->normalize($person, 'parent_test'));

Expected result: the value to be child-foo because this is the most concrete value.
Actual result: the normalized content is: parent-foo.

Support dynamic indication that properties should not be included

This issue is more like a try-out; We should look into adding an option to support passing context to the normalizer.

This context can contain, next to the group, an option that has property names that should be included. When passing this option the max_depth is set to 0.

Cache serialised/normalized data completely, if unchanged

Currently the normalized data is cached within the same session. If a new application session starts, the normalizer needs to re-normalize all the methods and properties.

Is there a way to keep the normalized data over application sessions?

(Cache data with the Symfony cache interface?)

Make support for method annotations

The result of a method should also be available so dummy properties (that only serve as a holder for a callback method) are not needed anymore.

Pass-down normalizer groups

This issue suggests to add support for a passdownGroup option in the normalizer bundle. With this option, the groups passed down to nested objects can be manually defined.

The following PHP class example will demonstrate the use case:

class Bar
{
    /**
     * @Bos\Normalize(group={"api_v3", "id_only"})
     * 
     * @var int
     */
    public $id;

    /**
     * @Bos\Normalize(group={"api_v3"}, passdownGroup="id_only")
     * 
     * @var Foo
     */
    public $foo;
}

class Foo
{
    /**
     * @Bos\Normalize(group={"api_v3", "id_only"})
     * 
     * @var int
     */
    public $id;

    /**
     * @Bos\Normalize(group={"api_v3"})
     * 
     * @var string
     */
    public $name;
}

Calling the normalizer like:

$this->normalizer->normalize($bar, 'api_v3');

Would result in:

{
  "id": 1,
  "foo": {
    "id": 1
  }
}

Instead of:

{
  "id": 1,
  "foo": {
    "id": 1,
    "name": "test"
  }
}

Without this feature, a lot of manual groups might have to be defined to support simple use cases like above.

Callback not working correctly on nested objects

Whenever I add a normalize annotation on a nested object, the property suddenly requires a getter() method to be set, while it actually uses the specified callback.

<?php

namespace AppBundle\Entity\Invoice;

use BowlOfSoup\NormalizerBundle\Annotation as Bos;

class Invoice
{
    /** @Bos\Normalize(type="object") */
    protected $debtor;
}
<?php

namespace AppBundle\Entity\Debtor;

use BowlOfSoup\NormalizerBundle\Annotation as Bos;

class Debtor
{
    /** @Bos\Normalize(callback="getFullName", name="name") */
    protected $bosName;

    public function getFullName()
    {
        ...
    }
}

The following results in exception 'Exception' with message 'Unable to get property value. No get() method found for property bosName' in /full/path/to/vendor/bowlofsoup/normalizer-bundle/Service/PropertyExtractor.php:77

Whenever I add a dummy method called getBosName, it works. The same setup however does work on the main object...

[Doctrine proxy] Parent method should be checked when child method that does not contain annotations

When normalizing a doctrine proxy, normalizer annotations on the entity are not taken into account.
This is because the annotations are not present on the proxy class itself.

Child:

class ProxySocial extends Social implements Proxy
{
    /**
     * {@inheritdoc}
     */
    public function __load()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function __isInitialized()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function getFacebook()
    {
        return parent::getFacebook();
    }
}

Parent; Which is in case of Doctrine proxies the actual class with correct annotations:

class Social
{
    /**
     * @var string
     *
     * @Bos\Normalize(group={"default"})
     */
    private $facebook;

    /**
     * @return string
     *
     * @Bos\Normalize(name="facebook", group={"via-method"})
     */
    public function getFacebook()
    {
        return $this->facebook;
    }
}

If we call the normalizer it will only check the child (proxy) method for annotations.
$normalizer->normalize($proxySocial, 'via-method')

We should support looking up the parent if a doctrine proxy is used.

Make possible to inherit normalizer annotation from a parent class

class Bar
{
    /**
     * @Bos\Normalize()
     */
    public function getName(): string
    {
        return 'john wick';
    }
}
 
class Foo extends Bar
{
    /**
     * PHPDoc without @Bos\Normalize annotation
     */
    public function getName(): string
    {
        return 'John Wick';
    }
}

When normalizing getName() from Foo, nothing will output.

We should make an annotation that indicates that the parent Normalize() annotations should be inherited.

Example:

class Foo extends Bar
{
    /**
     * @Bos\InheritNormalize()
     */
    public function getName(): string
    {
        return 'John Wick';
    }
}

Exception on unknown properties in annotations

As a developer I want to have an exception when a property of an annotation does not exists (e.g. is wrongly spelled).

Example:

@bos/Normalize(groupp={"testgroup"});

should indicate that 'groupp' is a property that does not exist.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.