Coder Social home page Coder Social logo

rebolon / php-sf-flex-webpack-encore-vuejs Goto Github PK

View Code? Open in Web Editor NEW
114.0 10.0 31.0 13.57 MB

A simple app skeleton to try to make every components work together : symfony 5.* (latest stable at the date, but work with sf 4 and 3.3+ if you pull the right tag), symfony/flex, webpack-encore, vuejs 2.5.x, boostrap 4 sass

Home Page: https://www.richard.icu/

License: MIT License

CSS 0.09% JavaScript 11.21% Vue 9.36% PHP 52.59% HTML 2.95% TypeScript 19.95% SCSS 0.31% Twig 3.55%
symfony webpack vuejs single-page-app sass bootstrap javascript testcafe reactjs quasar

php-sf-flex-webpack-encore-vuejs's Introduction

Symfony sample

[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Rebolon/php-sf-flex-webpack-encore-vuejs/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Rebolon/php-sf-flex-webpack-encore-vuejs/badges/quality-score.png?b=master) [![DeepScan grade](https://deepscan.io/api/teams/2301/projects/3192/branches/26485/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=2301&pid=3192&bid=26485) [![Known Vulnerabilities](https://snyk.io/test/github/rebolon/php-sf-flex-webpack-encore-vuejs/badge.svg?targetFile=package.json)](https://snyk.io/test/github/rebolon/php-sf-flex-webpack-encore-vuejs?targetFile=package.json) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FRebolon%2Fphp-sf-flex-webpack-encore-vuejs.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FRebolon%2Fphp-sf-flex-webpack-encore-vuejs?ref=badge_shield) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FRebolon%2Fphp-sf-flex-webpack-encore-vuejs.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FRebolon%2Fphp-sf-flex-webpack-encore-vuejs?ref=badge_shield)

requirements

  • You need PHP (7.4+), composer, node (15+ is better) npm or yarn and @angular/cli package installed globally

  • You also need to configure your php with curl and openssl

  • You have to setup the certificates download pem file, put it somewhere on your system and set your php.ini with those values:

    • curl.cainfo = PATH_TO_YOUR_CERTIFICATE/cacert.pe
    • openssl.cafile = PATH_TO_YOUR_CERTIFICATE/cacert.pem

You can run the app with a PHP Built_in server (all npm command relys on it), but some features may not work finely. In fact, Api-platform package rely on some HTTP SERVER features that are not implemented by the PHP Built-in server. So the api indexes like /api/index.json, /api/index.jsonld or /api/index.html will just return a 404 Not Found. There is also the react-admin app generated by the Api-platform that will not work because it relies on api json index.

To run the tests:

  • you may need to download the chrome webdriver with the same version as your own Chrome. Here is the dowload page : https://chromedriver.chromium.org/downloads and then you can specify the Panther webdriver path in a .env.test.local (copied from .env.test) on the row PANTHER_CHROME_DRIVER_BINARY=
  • you also have to build the assets (npm run build) because some functional tests relies on real web page, so assets must be available

explanation

This application has been realized to get a sample front app with sf4+ & vuejs but it also shows basic controllers with what most developpers do : basic controller, controller with twig, http call to external API, logging, API... We will try to not use front manipulation outside of VueJS (the sample with twig are really basic and won't use form per example) Here is how it has been created:

  • composer create-project symfony/skeleton sf-flex-encore-vuejs
  • cd sf-flex-encore-vuejs
  • composer req encore annotations twig api jwt-auth symfony/http-client profiler log doctrine-migrations admin webonyx/graphql-php
  • composer require --dev doctrine/doctrine-fixtures-bundle phpunit/phpunit symfony/dom-crawler symfony/browser-kit symfony/css-selector security-checker roave/security-advisories:dev-master symfony/maker-bundle
  • yarn add vue vue-router quasar-framework quasar-extras vuelidate vue-apollo@next graphql apollo-client apollo-link apollo-link-http apollo-link-error apollo-cache-inmemory graphql-tag react react-dom prop-types axios rxjs @devexpress/dx-react-core @devexpress/dx-react-grid
  • yarn add --dev vue-loader vue-template-compiler vue-router react-loader babel-preset-env babel-preset-react sass-loader node-sass [email protected] jasmine karma karma-jasmine karma-spec-reporter karma-junit-reporter karma-webpack karma-chrome-launcher offline-plugin rxjs-tslint
  • yarn install

Then some php controllers has been created on following routes :

  • / : DefaultController with the menu to navigate throught different controllers
  • /demo/simple : SimpleController with route config in routes.yaml and logger injection with autowiring
  • /demo/hello/:name : HelloController with route config in annotations and twig template
  • /demo/http : HttpController to show how to call external API from your controller
  • /demo/login/standard/secured : LoginController for standard login by Symfony
  • /demo/login/jwt/authenticate : Lexik Jwt bundle entry point to process the login check Don't forget the header Content-type: application/json or the application will always return this error no controller found for /api/login_check
  • /demo/login/json/authenticate : LoginJsonController for json login with JS applications but in stateful context
  • /demo/login/jwt/frontend: LoginJwtController for jwt login with JS applications in a stateless context
  • /demo/vuejs : VuejsController with route config in annotations and VueJS app with specific js/css import
  • /demo/quasar : QuasarController like VuejsController but with the Quasar framework for UX components
  • /demo/form/quasar-vuejs : [Work in progress] authentification with javascript, and a full web application with vuejs and api-platform(rest/graphql)
  • /demo/form/devxpress-angular : Angular7 demo with DevXpress UI components (Datagrid and a Wizard sample with local storage and a sumup zone)
  • /demo/form/devxpress-vuejs : VueJS demo with DevXpress UI components (Datagrid)
  • /api : access ApiPlatform api doc (you need to be authentified: from POST HTTP call or from the frontend /demo/form if you want to play with it: only book entity is configured to be accessed with ROLE_USER)
  • /api/graphql : access ApiPlatform GraphQL implementation
  • /admin : the react admin provided by api platform package (more info here https://api-platform.com/docs/admin/getting-started) : take care, we forced version 0.6.2 because 0.6.3 has an issue with authentication
  • /ezadmin : use the easy admin bundle to allow a comparison between fullstack PHP and PHP/VueJS

And then we followed the documentation here from api platform to create the Admin React tool. We just proceed to some adjustments:

  • we kept only src folder and moved its content at the root of /assets/js/api-platform-admin-react
  • we added the authClient like they did on the documentation
  • on main component componentWillMount() we just do a first fecth on /token to retreive a valid csrf_token for next api calls

But, Vuejs, ReactJS and Angular together ? with Symfony4, WTF ??? Yes it can seems completely stupid to use all this technologies together, but don't forget one thing : this is a POC ! The aim is not to help you to mix all those techs, but just to help you to use some of them finely. The biggest problem in my case is the dependancy management : all those JS libraries may need the same deps but in different version... For instance it seems to be ok, but i think that in future it could be a real brain-teaser.

configuration

You can change the php executable (if you want to use a php version with xdebug, or just another version of php) using package.json in the config section. Default php uses the one in path if exists. You can also change the web server port and the asset server port in the same config section.

    "config": {
        "php": "php",
        "server_port_web": "80",
        "server_port_asset": "8080",
        "test_browser": "chrome:headless,firefox"
    },

The test_browser section represent all the browsers you want to use with the Panther testing tool (we previously used testcafe, but it changes toot much and results was not so satisfying).

components

  • flex: new symfony system to make web dev life easier ; it works with recipes
  • vuejs: top js framework to build SPA, or just widget on classic page
  • quasar: UX component library based on VueJS
  • encore: symfony solution to wrap webpack config and, once again, make your life simpler
  • offline-plugin: webpack plugin to manage offline assets
  • annotations: use annotations everywhere in your PHP code
  • twig: symfony template solution, useless if you don't want to render template with symfony, but usefull to be able to use assets twig helper with webpack encore
  • api: api-platform to build REST/GraphQL api (instead of fosrestbundle). For GraphQL you just need one more package from webonyx
  • react: js framework used here by api-platform for their react admin component, it's built on top of https://marmelab.com/admin-on-rest
  • @devexpress: UX components library for Angular, React, VueJS or VanillaJS, with commercial licence and also an open-source free under some conditions
  • symfony/http-client: a cool new library to do http call from http (delivered by Symfony in 2019 !)
  • doctrine-migrations: based on Doctrine ORM, it make it easy to change your db during a project life
  • doctrine-fixture: also based on Doctrine to help you to add fixtures in your DB (for your tests or for project init)
  • ocramius/proxy-manager: i don't directly need it, in fact it's doctrine migrations that needs it. But since the release 2.2, it requires PHP7.2.* and i want this project to be compliant with 7.1 so i force the version of this package until i decide to be only compliant PHP7.2
  • admin: easy admin component to build quick backend with auto form
  • profiler: for debugging purpose
  • log: a logger for symfony
  • maker-bundle: a bundle that i have not enough used. It will help you to manage your entities, your migrations... it's a must have to work with
  • security-checker: a tool to check known securities vulnerabilities, to use it, run php bin/console security:check
  • roave/security-advisories: a tool that prevent the install of PHP package from composer with known vulnerabilities
  • phpunit, crawler, browserkit, css-selector: php/symfony task for testing (@todo ll last 3 should be a recipe)
  • alice: ApiPlatform recommend its usage for testing rollback with traits RefreshDatabaseTrait. Personnaly i don't use the main features which is about Fixtures. It requires a new learning steps whereas fixtures should be something easy. It's up to you to learn it, or not ;-)
  • panther: the symfony toolkit to manage testing throught a browser (before this component, we had to use npm libraries to control browser)
  • babel-preset-env: do you really need explanation ? this is just for Babel7
  • testcafe: an e2e test framework (might be changed with chimp or anything else, gimme better idea)
  • jasmine & karma: a stack for unit & e2e tests (a more standard stack to replace testcafé)
  • sass: hey, we are not in nineties, we don't write css now
  • bootstrap: the 4th version of the first class css framework (not used with quasar). The bad thing is that it requires Jquery... that's why i import jquery-slim
  • axios: the library to perform http calls
  • rxjs: THE library to replace the usage of Promise !

Here are some non installed components that may help you:

  • rekit: an IDE for React devlopment, you should install it globally

For Angular (v5), i decided to do quite different way:

  • i initialized a project with angular-cli (first install it globally yarn add -g @angular/cli) like this: cd assets/js && ng new devxpress-angular && cd devxpress-angular && yarn install
  • then i customize the .angular-cli.json to set output path to my root public/dist-ng folder
  • i set this dist-ng folder because encore will remove dist folder when we run some command so with different dist folders i don't have any problem
  • i customize my npm watch scripts and set a new watch:windows which allows to run the 2 watcher (one for encore, and one for angularCli) at the same time with only one npm scripts
  • i decided to not use npm run serve but npm run build with watch options because this last one is the only way to generate files in dist-ng folder. ng serve does everything in memory only

run

  • clone the project like this git clone [email protected]:Rebolon/php-sf-flex-webpack-encore-vuejs.git and go into this directory
  • install the project with npm run init-project (or init-project:w for windows system) which will launch :
    1. copy the env file (or set them on your system) : cp .env.dist .env
    2. php dependancies installation: composer install
    3. nodejs tooling installation (and angular deps): npm install && cd assets/js/form-devxpress-angular && npm install
    4. assets generation: npm run dev
    5. db init: php bin/console doctrine:database:create & php bin/console doctrine:schema:create & php bin/console doctrine:fixtures:load
      • it creates the physical database from the config/packages/doctrine.yaml file
      • it builds the schema based on src/Entity/* files
      • it load fixtures based on a db sample built with calibre software plus a plugin that export data to sqlite format. An alternative to this would have been to use https://github.com/hautelook/AliceBundle and build yaml fixtures, but i already had an sqlite db so i didn't need this :-)
  • generate certificate for JWT authentification:
    1. Change the value of the key JWT_PASSPHRASE from the file .env
    2. Run npm run jwt-init and use the passphrase you setup for JWT_PASSPHRASE
  • Run your application with php built-in server:
    1. Change to the project directory

    2. Execute the npm run build that will build assets and watch for angular app change (if you have a node-sass error Node Sass does not yet support your current environment..., you may need to run npm rebuild node-sass)

    3. Execute the npm run dev-server-hot (or dev-server-hot:w for windows system) command to start the asset server that will build your assets for vue and react and your manifest.json and serve the assets with hot module replacement when you do a modification on a vuejs file

    4. Execute the npm run sf-dev (or sf-dev:w for windows system) command;

    5. Browse to the http://localhost:80/ URL.

      • Run composer require symfony/web-server-bundle for a better web server.
      • Quit the server with CTRL-C.
      • And launch php bin/console server:start 127.0.0.1:80
    6. Run frontend tests with npm run test

Don't forget to prefer an nginx/apache server to be able to use full features of api-platform.

If you want to change default ports, you can use package.json > config : server_port_web for the web server (php built in server), and server_port_asset for the asset server. Default ports are 80 and 8080.

If you update the project:

  • Don't forget to run doctrine:migrations:migrate to take care of DB modifications.
  • Do the following command npm run dump-js-config (or dump-js-config:win for windows system) to create the js config file

To make it easy to test your API, i added a file POSTMAN.json that you can import into Postman cli. Then just configure your postman environement with the rights variable (the host...). Then run the login route, copy the bearer into your postman environment variable and then you will be able to test all protected route.

webpack

Everything is managed by 'encore' symfony package, so have a look at the webpack.config.js and then read their docs

  • npm run dev : will build your assets (in this project it's /public/build/)
  • npm run watch : does the same thing than npm run dev, but it watches files modification to re-generate the assets
  • npm run dev-server : build the manifest.json that map your assets qith their url from the asset server and start a web server that will serve those assets
  • npm run dev-server-hot : does the same thing as previously, but with vuejs framework it also does Hot Module Replacement
  • npm run build : build your assets for production

Take care, the asset server listen to port 8080 so don't start your main server on that port, or specify another port for the dev-server using --port 9999 for example

Also, if you want to use the asset server finely, you have to add the assets configuration in the config/packages/framework.yaml file : json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'. In fact the npm command will build asset in memory only, and modify the manifest file to map asset to a new url served by the asset server instead of the main web server.

In the main layout, we load 3 common files: manifest.js, vendor.js and service-worker.js. Vendor is where you wan put all common libraries used on almost all pages. The source file for this bundle is assets/js/app.js. SW is for the service workers. It's default behavior is to manage the Cache file. You can have a look at offline-plugin for webpack.

code quality

PHP

The project uses 2 packages to lint and fix the code style of PHP code :You can install phpcs to check your code

  • squizlabs/PHP_CodeSniffer to lint and follow PSR1/PSR2 rules.
  • friendsofphp/php-cs-fixer to fix the code

Lint with this command vendor/bin/phpcs src -n --standard=PSR1,PSR2 --report=summary to get a summary of errors. You can fix it with vendor/in/phpcbf src -n --standard=PSR1,PSR2 which is delivered with phpcs, but it doesn't fix everything so i suggest to use php-cs-fixer with this command vendor/bin/php-cs-fixer fix src --rules=@PSR1,@PSR2

Javascript

For Javascript the following packages has been used: npm install prettier

To lint the code: node bin-prettier.js assets/js/** To fix it: node bin-prettier.js assets/js/** --write

IDE

For PHP you should configure your IDE to follow PSR1/PSR2 code style (or anything else if you prefer). For JS you will have to install prettier tool.

Tests

On PHP we use PHPUnit and Symfony web testcase. We have basic tests on PHP that will try to test that we have HTTP 200 OK on each routes. We should also tests Commands and other classes, but we should also test more finely the API content. Take care with Symfony4 to configure the config/packages/test/framework.yaml file to overload the session.storage_id with a mock !

JWT Certificates in test mode, we don't use pem files from your dev/prod env. In fact we use pem files generated specifically for tests and copied from var/travis/config to var/cache/config

On Javascript we have unit and e2 tests. Units tests are managed by jasmine and karma. It allows to test function, class, component. For e2e tests we use testcafe from devExpress. It allows to launch browsers and drive them by code to reproduce a human behavior. Here the tests runs on a chrome headless, and firefox but you can configure it in the package.json file in the config.test_browser node. We now use Panther to run e2e tests. It's automatically ran when we run phpunit commands.

npm run test-karma will run js test and will generate a karma_report.xml files in the following folder /var/report/.

If you wonder how to tests your VueJS components, you can hae a look at this website which describe a lot of tests. Sadly it's in french !

In this application i used Karma for one application with VueJS (in /assets/js/vuejs). And i used Panther to test pages fully generated by Symfony. Karma is configured with assets/tests/units/karma.conf.js !

Security

On PHP i use those 2 packages to prevent the use of deprecated packages or with vulnerabilities:

  • security-checker: when you run php bin/console security:check it will checks your dependancies vs known vulnerabilites. You should use it on existing project.
  • roave/security-advisories: it will prevent the composer require and update command on package with known vulnerabilities. But it won't prevent the composer install with an existing composer.lock so you are safe to deliver existing project.

On JS i use snyk services.

@TODO finish on PHP and JS checks + tools to audit the code + software that analyse sql/xss/file injection, csrf, ... @TODO explain the usage of tools like OWASP ZED, sqlmap, php avenger... @TODO help to setup security system: stateful app = take care at csrf ; stateless app = should i use jwt, api key, OAuth, anything else ?

Don't forget to use HTTPS, even in local to help you find errors that will happen in production. One certificate has been generated for localhost (with http://www.selfsignedcertificate.com/) and is available in /var/certificates/.cert|.key There is a simple nginx conf (used for travis CI) that use those certificates so you can use nginx to work (just don't forget to change the port that is fixed to 80 like setup in the package.json). Travis will no more be supported in 2021 because they change their business model. I may move on gitlabCI in future.

TestCafé for functional testing generate an error when you don't use ssl: Uncaught (in promise) DOMException: Only secure origins are allowed (see: https://goo.gl/Y0ZkNV). But for instance i didn't found any solution to run it finely without --skip-js-errors parameters.

Symfony security

In Symfony i configured different firewalls:

  • security_js and security_php share the same context so when you are logged on one, you are also logged on the other. I did this because a firewall cannot use both form_login and json_login (or i didn't found the way), and i wanted you to understand the concept of context.
  • security_jwt is for stateless app. You can read the following tutorial to understand https://knpuniversity.com/screencast/symfony-rest4

If you doesn't need JWT, you can use ApiKey pattern. For this you have to implement the required Authenticator: https://symfony.com/doc/current/security/api_key_authentication.html If you need more tuning with huge APIs, you may require OAuth with JWT. OAuth will help you so have a look at this website: https://oauth.net/

Cookies

In statefull app you will have HTTP Cookies. Even if you don't really manipulate them, and that they are used only to transport the sessionId between the server and the client. But if you use them to store other informations, you should be aware that they can be misused and that they can open XSS or CSRF web fail. Your cookies must have some specific attributes:

  • secure
  • httponly
  • date (to prevent them to be infinite)
  • samesite (strict or lax...)

You have to pay attention at them. And you can play with them on those sites: http://cookies.rocks and http://example-bar.com (source here: https://github.com/hsablonniere/cookies.rocks and related talk here: https://github.com/hsablonniere/talk-back-to-basics-cookies)

API

Take care at the custom listeners that you could write based on Api-Platform documentation. They are used by all controllers, not only those from ApiPlatform.

Security

In /api you can test the API with the swagger interface. Because the route uses JWT token you have to call the /demo/security/login/jwt/tokens route with HTTP POST and Basic Auth. Login is test_php or test_js and password is test ! Then in the swagger interface click on the upper right button Authorization and add 'Bearer THETOKENRECEIVED'

To get a valid JWT token you can use the lexik command : php bin/console lexik:jwt:generate-token You can also provide a username/password to the command, i let you read the help of the command to know how-to do.

When using GraphiQL with secured resources, you won't be able to provide any token (i didn't find any clue for this). So i found this good tool to query my API by providing required Authorization header: https://altair.sirmuel.design/.

Sort

If you want to allow sorting based on columns, you will have to add Filter annotations on Entity. Look at the Book entity and its ApiFilter which allow to sort on id and title. Then you will be able to call the api like this: http://localhost/api/books?order[id]=DESC

Filter

Sometime you will want to filter on some fields: i want Reviews published after xxx. For things like this you have to add new ApiFilter (like we did with sort). There is a sample in the Review entity

You can also filter on relation, but in that case you will have to use the id (or the iris) of the relation as the value of the filter. More information in documentation of ApiPlatform.

Normalizer, or how to return data from nested entities

At the beginning when you query a Books route, each nested entities like Editors, Series, Authors will be represented only by IRI like : "/api/serie/1" This is cool when you don't need all nested information, you will prevent the user to download too many data. But very often, your application will require those informations, and in that case you seem condamned to do further HTTP call to retrieve all sub entities.

You are on the wrong way, and i was too !

Test the route /api/books and you will retreive that kind of response:

[
  {
    "id": 1,
    "title": "20th Century Boys (Deluxe), Tome 1 test",
    "description": null,
    "indexInSerie": 1,
    "authors": [
      {
        "id": 1,
        "role": {
          "id": 1,
          "translationKey": "JOB_WRITER"
        },
        "author": {
          "id": 1,
          "firstname": "Urasawa"
        }
      }
    ],
    "editors": [
      {
        "id": 1,
        "publicationDate": "0101-01-01T00:00:00+00:09",
        "collection": null,
        "isbn": "",
        "editor": {
          "id": 1,
          "name": "Panini Comics"
        }
      }
    ],
    "serie": {
      "id": 1,
      "name": "20th Century boys (Deluxe)"
    }
  },
  ...
]

You can tell your API to return those nested entities. Look at Book and Serie entities. You will find extra annotation with @Groups and attributes. In Book, look at the main annotation, and at related property (i only show title property but more are aimed by @Groups, look at original code here: https://github.com/Rebolon/php-sf-flex-webpack-encore-vuejs/blob/master/src/Entity/Library/Book.php):

/**
 * @ApiResource(
 *     ...
 *     attributes={
 *          "normalization_context"={
 *              "groups"={"book_detail"}
 *          }
 *     }
 * )
 *
 * @ORM\Entity
 */
class Book implements LibraryInterface {
...
    /**
     * @Groups("book_detail")
     *
     * @ORM\Column(type="string", length=255, nullable=false)
     *
     * @Assert\NotBlank()
     * @Assert\Length(max="255")
     *
     */
    private $title;
...

And now the Serie entity, wher you only need to add @Groups on the properties you want to be returned:

...
    /**
     * @Groups("book_detail")
     *
     * @ORM\Column(type="string", length=512, nullable=false)
     *
     * @Assert\NotBlank()
     * @Assert\Length(max="512")
     */
    private $name;
...

Have a look at Kevin Dunglas slides because it gimme some clue for this feature: https://speakerdeck.com/dunglas/rest-vs-graphql-illustrated-examples-with-the-api-platform-framework His talk may be online in future but i don't kno when ;-) so here is the page that may link to the video, on day: https://github.com/SymfonyLive/paris-2018-talks

I still need to work on this feature because it would be cool to be able to build different route that may return IRIS or Normalized data. I don't know if it's possible and maybe i should go to GraphQL API because that's its job to do that kind of thing with its "Query".

Custom Route

There is a sample of custom route based on Action Demand Responder pattern that will allow to create new Books and it's dependancies in one HTTP call. You won't need to create sub-entity before creating the main one, said the book. The endpoint is /api/booksiu/special_3 [POST]. It takes the following JSON string as Body:

// The most complete sample, with de-duplication of editor (only once will be created)
{
    "book": {
        "title": "Zombies in western culture",
        "editors": [{
            "publication_date": "1519664915",
            "collection": "printed version",
            "isbn": "9781783743230",
            "editor": {
                "name": "Open Book Publishers"
            }
        }, {
            "publication_date": "1519747464",
            "collection": "ebooks",
            "isbn": "9791036500824",
            "editor": {
                "name": "Open Book Publishers"
            }
        }],
        "authors": [{
            "role": {
                "translation_key": "WRITER"
            },
            "author": {
                "firstname": "Marc",
                "lastname": "O'Brien"
            }
        }, {
            "role": {
                "translation_key": "WRITER"
            },
            "author": {
                "firstname": "Paul",
                "lastname": "Kyprianou"
            }
        }],
        "serie": {
            "name": "Open Reports Series"
        }
    }
}

// This one re-use database information for editor / author / job / serie
{
    "book": {
        "title": "Oh my god, how simple it is !",
        "editors": [{
            "publication_date": "1519664915",
            "collection": "from my head",
            "isbn": "9781783742530",
            "editor": 1
        }, {
            "publication_date": "1519747464",
            "collection": "ebooks",
            "isbn": "9782821883963",
            "editor": {
                "name": "Open Book Publishers"
            }
        }],
        "authors": [{
            "role": 2,
            "author": 3
        }, {
            "role": {
                "translation_key": "WRITER"
            },
            "author": {
                "firstname": "Paul",
                "lastname": "Kyprianou"
            }
        }],
        "serie": 4
    }
}

todo

  • setup Sf4
  • setup symfony/webpack-encore
  • db: add a model with doctrine entities
  • db: add db fixtures at init! and fixtures for test only: done with the DoctrineFixturesBundle, it will generate fixtures based on var/data/fixtures.db, and when called from test env it will build a small scope of fixtures
  • api: configure ApiPlatform
  • api: setup one custom route for ApiPlatform
  • api: replace IRIs for nested entities by real data (normalizer usage)
  • api: setup custom route with nested objects on create/update/read (read should be solved ith serializer, create/update might be solved with custom route or DTOS so try 2 ways)
  • api: those custom route must referenced by the API documentation on /api route with correct description (for instance it's just book: string ...)
  • api: use normalizer to get nested entities
  • api: use denormalizer to post/put nested entities (and use custom route when we want deduplication for example)
  • api: use properties in queryString to select only wished props from REST: api/books/1?properties[]=title&properties[]=serie
  • api: improve JSON error from custom route: for instance when editor is misfilled it just return this message (without property path): jsonOrArray can be string or array
  • api: graphQL: multiple queries in one call ?
  • api: graphQL: multiple mutations in one call ?
  • api: graphQL: how to mutate nested objects in a minimal call ?
  • api: check best security system to setup with ApiPlatform (JWT / ApiKey / cookie & csrf system but in that case we are stateful which is not cool for deployment and replication ?). Finally we use JWT which is the best thing to do and compliant with statefull or stateless.
  • api: JWT setup the pattern for the refresh-token or anything else more info here https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/ : when getting a 401 from api it should tells more information: does the token is valid or not ?
  • front: setup VueJS
  • front: use Quasar with VueJS
  • front: move on Quasar 0.15.x
  • front: move on Quasar 0.17.x
  • front: move on Angular 6 with HttpInterceptor, Guards, SSR
  • front: Angular6 with prod build, the assets (js/css) are not wel loaded coz they are generated with versoning hash whereas the twig template load them in hard without hash (no manijest may help the asset helper)
  • front: Update to Angular7
  • front: improve bundle size for Angular (for instance there is a 4.7Mb vendor.js file which may be devxtrem full deps whereas we use only some widgets)
  • front: move on Devxtreme 18.x and use it with VueJS
  • front: setup CSRF protection with VueJS app
  • front: migrate app 'form-devextrem-angular' to angular6 when there will be compatible (this thread may helps: https://stackoverflow.com/questions/48970553/want-to-upgrade-project-from-angular-v5-to-angular-v6)
  • quality: setup unit tests for JS (karma/jasmine)
  • quality: setup e2e tests for JS (testcafé)
  • quality: setup phpunit tests for PHP (unit test and webtestcase)
  • quality: write some JS units tests
  • quality: write some JS e2e tests
  • quality: write some PHP tests
  • quality: fix testcafe role where sometimes they are not played: https://testcafe-discuss.devexpress.com/t/role-sometime-it-doesnt-seem-to-be-played/875
  • quality: setup tests reports
  • security: setup security with Symfony (ticket open coz i get 500 instead of 403: symfony/symfony#25806 Still WIP at this time) and choose between cookie (stateful), JWT (with Lexik bundle) or ApiKey (https://symfony.com/doc/current/security/guard_authentication.html) : I implement both statefull and JWT samples, but i didn't take the time for ApiKey
  • back: setup EasyAdminBundle
  • back: improve EasyAdminBundle with custom screen
  • back: setup React Admin from ApiPlatform (take care at this issue is mandatory api-platform/api-platform#584)
  • back: fix React Admin Book creation: it allows multiple authors (projectBookCreation) whereas it should not, and try to display the name of the author/editor... instead of their iris which is unundertandable
  • back: customize React Admin to display more information on datagrid, and customize form in book edition per example
  • back: keep EasyAdminBundle or React Admin: make a choice coz both are doing the same, i need to measure differences, and also the ease to do custom screen (change forms, manage rights...)
  • front: create another route with VueJS that use GQL instead of REST
  • front: add storyBook package for VueJS and Angular and use it to manage and test our components
  • quality: code style: use phpcscbf instead of php_cs_fixer coz it's embeded with phpcs and it uses the phpcs config file: I decided to keep php_cs_fixer because it's more complete!
  • quality: transform this project into a meta package that will install all requirements for JS app within Symfony (like does laravel)
  • security: check if i need the JMSSerializerBundle or if the serializer component is enough (if autowiring runs well, why not): I prefer to use Symfony serializer, it's enough
  • db: have a lookAt the HauteLookAliceBundle to help in the creation of real fixtures during tests (instead of generating a new test.db which could be long)
  • api: try https://github.com/overblog/GraphQLBundle instead of ApiPlatform to try nested query/mutations (resolver are not auto-generated)
  • quality: use a server logger for both JS and PHP (and also maybe HTTP, DB, MessageQueuing, ...), it will helps to improve quality of the app by identifing users system/browser and most current errors (Sentry or other service must be tested https://www.slant.co/options/964/alternatives/~sentry-alternatives)
  • front: move on babel 7 with babel-preset-env (remove all related babel from readme and read babeljs.io for more info on update)
  • improve this tutorial with an API Route built with Api platform (without DB) and install the vue-generator from api-platform for a crud sample
  • manage Entity orphanRemoval / CASCADE onDelete
  • find a good way to add a pre-commit hook that lint PHP and JS code, and run the PHP / JS tests
  • Here is a sample of GraphQL query with 2 queries in one call but i don't know how to manage this with Vue-apollo (he can't accept this coz it maps the result to the data[nameOfapolloQuery] whereas i have 2 names...) ? IN FACT the solution is the result hook (see vuejs/apollo#15, and the documentation)
query getBooksAndSerieQry($firstBook: Int, $afterBook: String, $firstSerie: Int, $afterSerie: String) {
    getBooksAndSerie: books(first: $firstBook, after: $afterBook) {
        edges {
            node {
                id
                title
            }
            cursor
        }
        pageInfo {
            endCursor
            hasNextPage
        }
    }
    getBooksAndSerie: series(first:$firstSerie, after: $afterSerie) {
        edges {
            node {
                name
            }
            cursor
        }
        pageInfo {
            endCursor
            hasNextPage
        }
    }
}

extras info

I wrote some articles on medium to explain some practices setup in this project:

third party issues that helped me to go on

Third-part licencing

License

FOSSA Status

php-sf-flex-webpack-encore-vuejs's People

Contributors

fossabot avatar marwahaha avatar rebolon avatar scrutinizer-auto-fixer avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-sf-flex-webpack-encore-vuejs's Issues

Angular (and other JS app)

When i restore an object from cache, only the root Object is a real entity. If some props are related to other Entities, they will just be JSON object, not real instance of an Entity.
ex.

const jsonBook = {
  "title": "Harry Potter 1", 
  "serie": {
    "name": "Harry Potter"
  }
}

when i do a Book.initializeFromJson(Book, jsonBook), i will get a Book instance with all its own methods an props, but it's serie property won't be a Serie instance, just a javascript object.

TokenAuthentificator: check its behavior

It must authenticate a user based on a query.
If there is credentials it must check it
If there is no credentials it must not check them

  • maybe it may only support the login_json route

  • then check that other routes are running

  • or it may check all routes except GET

  • on login route it must check credentials

  • on other route it must only check validity of CSRF token

  • This 2nd option is to automate csrf check on POST/PUT/PATCH/DELETE query, because developper doesn't always use csrf

I also need to change the name: CSRFTokenAuthentificator

Add usage of CSRF token

Create a form with vuejs and a POST route in SF
Use CSRF token : store it in JS or in HTML header, and find a way to send it with every request to the server (not with request to external app like heroku)

React admin fails to load

go to /demo/api-platform-admin-react
open the console

invariant.js:42 Uncaught Error: Element ref was specified as a string (listItem) but no owner was set. You may have multiple copies of React loaded. (details: https://fb.me/react-refs-must-have-owner).

Custom Search: OR clause

With Api Platform, how could i manage an OR clause ?
I want to search Author on firstname or lastname, how may i do this ?
Should i use a DB View, map an entity on it (which would extends the Author entity) and add an extra virtual field name which concat both firts and last ?

PHP tests are failing coz of deps deprecations

Here is the console output

  8x: A tree builder without a root node is deprecated since Symfony 4.2 and will not be supported anymore in 5.0.
    8x in ToolsAbstract::setUpBeforeClass from App\Tests\Common

  6x: Passing a RoleHierarchyInterface to "Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter::__construct()" is deprecated since Symfony 4.2. Pass an AuthorizationCheckerInterface instead.
    4x in HTTP200WebPagesTest::testHttp200OnAllPages from App\Tests\Controller
    1x in LoginJsonTest::testLogin from App\Tests\Controller
    1x in LoginJwtTest::testLogin from App\Tests\Controller

  2x: Using a string "HTTP_BAD_REQUEST" as a constant of the "Symfony\Component\HttpFoundation\Response" class is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3. Use the Sy
mfony's custom YAML extension for PHP constants instead (i.e. "!php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST").
    2x in ToolsAbstract::setUpBeforeClass from App\Tests\Common

  2x: The "lexik_jwt_authentication.private_key_path" configuration key is deprecated since version 2.5. Use "lexik_jwt_authentication.secret_key" instead.
    2x in ToolsAbstract::setUpBeforeClass from App\Tests\Common

  2x: The "lexik_jwt_authentication.public_key_path" configuration key is deprecated since version 2.5. Use "lexik_jwt_authentication.public_key" instead.
    2x in ToolsAbstract::setUpBeforeClass from App\Tests\Common

  1x: Enabling the "sensio_framework_extra.router.annotations" configuration is deprecated since version 5.2. Set it to false and use the "Symfony\Component\Routing\Annotation\Route" annotation from Symfony its
elf.
    1x in ToolsAbstract::setUpBeforeClass from App\Tests\Common

  1x: The "Symfony\Component\Translation\MessageSelector" class is deprecated since Symfony 4.2, use IdentityTranslator instead.
    1x in ToolsAbstract::setUpBeforeClass from App\Tests\Common

  1x: The "Sensio\Bundle\FrameworkExtraBundle\Configuration\Method" annotation is deprecated since version 5.2. Use "Symfony\Component\Routing\Annotation\Route" instead.
    1x in HTTP200WebPagesTest::testLogin from App\Tests\Controller

  1x: The "sensio_framework_extra.routing.loader.annot_class" service is deprecated since version 5.2
    1x in HTTP200WebPagesTest::testHttp200OnAllPages from App\Tests\Controller

  1x: The "Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader" class is deprecated since version 5.2. Use "Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader" instead.
    1x in HTTP200WebPagesTest::testHttp200OnAllPages from App\Tests\Controller

  1x: The "sensio_framework_extra.routing.loader.annot_dir" service is deprecated since version 5.2
    1x in HTTP200WebPagesTest::testHttp200OnAllPages from App\Tests\Controller

  1x: The "sensio_framework_extra.routing.loader.annot_file" service is deprecated since version 5.2
    1x in HTTP200WebPagesTest::testHttp200OnAllPages from App\Tests\Controller

  1x: Twig Filter "transchoice" is deprecated since version 4.2. Use "trans" with parameter "%count%" instead in C:\dev\projects\me_and_poc\php-sf-flex-webpack-encore-vuejs\vendor\easycorp\easyadmin-bundle\src\
Resources\views\default\list.html.twig at line 34.
    1x in HTTP200WebPagesTest::testHttp200OnAllPages from App\Tests\Controller

  1x: Twig Filter "transchoice" is deprecated since version 4.2. Use "trans" with parameter "%count%" instead in C:\dev\projects\me_and_poc\php-sf-flex-webpack-encore-vuejs\vendor\easycorp\easyadmin-bundle\src\
Resources\views\default\list.html.twig at line 35.
    1x in HTTP200WebPagesTest::testHttp200OnAllPages from App\Tests\Controller

  1x: Twig Filter "transchoice" is deprecated since version 4.2. Use "trans" with parameter "%count%" instead in C:\dev\projects\me_and_poc\php-sf-flex-webpack-encore-vuejs\vendor\easycorp\easyadmin-bundle\src\
Resources\views\default\field_association.html.twig at line 20.
    1x in HTTP200WebPagesTest::testHttp200OnAllPages from App\Tests\Controller
  • easyAdminBundle is responsible of 3 last Twig alerts
  • others are my own responsibility

Needs some help to resolve them quickly coz i don't have time

set env for javascript in config.js

No idea on how to do this:

  • make an SF2 command to parse .env file and write it into config.js and finally run it with npm command ?
  • parse it with npm package ?

isLoggedIn always ask for a csrf token

It should not require it
Symfony security should retreive the user using the session_id and let the user go to the controller.

For instance the TokenAuthenticator take the request and reject it because it doesn't find the csrf parameter token. The behavior is falsy

EasyAdmin: Form Book fail because Catchable Fatal Error: Object of class App\Entity\Library\ProjectBookCreation could not be converted to string

uri to reproduce this :
http://localhost/admin/?action=new&entity=Book&menuIndex=0&submenuIndex=-1&sortField=id&sortDirection=DESC&page=33&referer=%252Fadmin%252F%253Faction%253Dlist%2526entity%253DBook%2526menuIndex%253D0%2526submenuIndex%253D-1%2526sortField%253Did%2526sortDirection%253DDESC%2526page%253D33

In fact a book is linked to editor and author using tables projectBookEdition and projectBookCreation. EasyAdmin try to allow the user to select items from thos 2 tables using selectBox. But it cannot, and in fact i dislike this because it would be better to add Author and Editor adding specific fields required by the associative tables.

encore dev works only if angular/cli 1.7.4 is installed

Working on feature/move-on-angular6 i wanted to remove root installation of angular/cli, coz it's useless.
But in fact i saw that without this devDeps, the command encore dev (or production) failed with this messages:

Running webpack ...

 ERROR  Failed to compile with 8 errors                                                                                                                                                               17:11:14

This dependency was not found:

* quasar-framework/dist/umd/quasar.mat.css in ./assets/js/quasar/app.js, ./assets/js/form-quasar-vuejs/app.js and 1 other

To install it, you can run: npm install --save quasar-framework/dist/umd/quasar.mat.css


These relative modules were not found:

* ./assets/css/app.scss in multi ./assets/js/app.js ./assets/css/app.scss
* ./index.css in ./assets/js/api-platform-admin-react/index.js
* ./material-icons/material-icons.css in ./node_modules/quasar-extras/material-icons.js
* ./roboto-font/roboto-font.css in ./node_modules/quasar-extras/roboto-font.js
* ./fontawesome/fontawesome.css in ./node_modules/quasar-extras/fontawesome.js

The first error can be dropped using quasar-framework/dist/quasar.mat.styl instead of umd version.
But i don't understand the next 5 errors. I checked, and i can say that relatives modules not found really exists. So what ?

Move on Angular 6

I don't know if devxtrem is compliant with that version but i need to use an Angular6 version of the app.

Test fail: ApiPlatformCustomRoutesWithParamConverterTest::testBookSpecialSample3WithErrors

I don't know if it's because of a version of PHP i'm using...
I have to find since when this test is failing
It should not coz this is the reviver library that manage this part normally

1) App\Tests\Normalization\ApiPlatformCustomRoutesWithParamConverterTest::testBookSpecialSample3WithErrors
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'book.editors[0].editor: jsonOrArray for editor must be string or array'
+'book.editors[0].editor: Only variables should be passed by reference'

It lacks JWT login system

Need to add the route inside the DefaultController

  • explain in the readme how to login, and how to get a new valid token

Quasar icons seems to not be used by browser

With PHPBuiltInServer, when i go to the page Form with Quasar and VueJS, no icons are rendered. The toast on login is crappy, the eye in the password fied is not clickable...

my Hydra config is wrong and lead to an error : try to get list of author with /api/authors

Hydra error :

{
  "@context": "/api/contexts/Error",
  "@type": "hydra:Error",
  "hydra:title": "An error occurred",
  "hydra:description": "A circular reference has been detected when serializing the object of class \"App\\Entity\\Library\\ProjectBookCreation\" (configured limit: 1)",
  "trace": [
    {
      "namespace": "",
      "short_class": "",
      "class": "",
      "type": "",
      "function": "",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Normalizer\\AbstractNormalizer.php",
      "line": 195,
      "args": []
    },
    {
      "namespace": "Symfony\\Component\\Serializer\\Normalizer",
      "short_class": "AbstractNormalizer",
      "class": "Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer",
      "type": "->",
      "function": "handleCircularReference",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Normalizer\\AbstractObjectNormalizer.php",
      "line": 66,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\ProjectBookCreation"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer\\Normalizer",
      "short_class": "AbstractObjectNormalizer",
      "class": "Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Serializer.php",
      "line": 133,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\ProjectBookCreation"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b970000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer",
      "short_class": "Serializer",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\CollectionNormalizer.php",
      "line": 69,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\ProjectBookCreation"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b970000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "CollectionNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\CollectionNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\PartialCollectionViewNormalizer.php",
      "line": 49,
      "args": [
        [
          "object",
          "Doctrine\\ORM\\PersistentCollection"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b970000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "PartialCollectionViewNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\PartialCollectionViewNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\CollectionFiltersNormalizer.php",
      "line": 65,
      "args": [
        [
          "object",
          "Doctrine\\ORM\\PersistentCollection"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b970000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "CollectionFiltersNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\CollectionFiltersNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Serializer.php",
      "line": 133,
      "args": [
        [
          "object",
          "Doctrine\\ORM\\PersistentCollection"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b970000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer",
      "short_class": "Serializer",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Serializer\\AbstractItemNormalizer.php",
      "line": 445,
      "args": [
        [
          "object",
          "Doctrine\\ORM\\PersistentCollection"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b970000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Serializer",
      "short_class": "AbstractItemNormalizer",
      "class": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer",
      "type": "->",
      "function": "getAttributeValue",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Normalizer\\AbstractObjectNormalizer.php",
      "line": 80,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Book"
        ],
        [
          "string",
          "projectBookCreation"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b970000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer\\Normalizer",
      "short_class": "AbstractObjectNormalizer",
      "class": "Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Serializer\\AbstractItemNormalizer.php",
      "line": 100,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Book"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b970000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Book"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Serializer",
      "short_class": "AbstractItemNormalizer",
      "class": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\JsonLd\\Serializer\\ItemNormalizer.php",
      "line": 73,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Book"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Book"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\JsonLd\\Serializer",
      "short_class": "ItemNormalizer",
      "class": "ApiPlatform\\Core\\JsonLd\\Serializer\\ItemNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Serializer.php",
      "line": 133,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Book"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/books/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Book"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer",
      "short_class": "Serializer",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Normalizer\\AbstractObjectNormalizer.php",
      "line": 98,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Book"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer\\Normalizer",
      "short_class": "AbstractObjectNormalizer",
      "class": "Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Serializer.php",
      "line": 133,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\ProjectBookCreation"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ],
                "00000000396b5b880000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer",
      "short_class": "Serializer",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\CollectionNormalizer.php",
      "line": 69,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\ProjectBookCreation"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "CollectionNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\CollectionNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\PartialCollectionViewNormalizer.php",
      "line": 49,
      "args": [
        [
          "object",
          "Doctrine\\ORM\\PersistentCollection"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "PartialCollectionViewNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\PartialCollectionViewNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\CollectionFiltersNormalizer.php",
      "line": 65,
      "args": [
        [
          "object",
          "Doctrine\\ORM\\PersistentCollection"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "CollectionFiltersNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\CollectionFiltersNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Serializer.php",
      "line": 133,
      "args": [
        [
          "object",
          "Doctrine\\ORM\\PersistentCollection"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer",
      "short_class": "Serializer",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Serializer\\AbstractItemNormalizer.php",
      "line": 445,
      "args": [
        [
          "object",
          "Doctrine\\ORM\\PersistentCollection"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Serializer",
      "short_class": "AbstractItemNormalizer",
      "class": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer",
      "type": "->",
      "function": "getAttributeValue",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Normalizer\\AbstractObjectNormalizer.php",
      "line": 80,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Author"
        ],
        [
          "string",
          "books"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer\\Normalizer",
      "short_class": "AbstractObjectNormalizer",
      "class": "Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Serializer\\AbstractItemNormalizer.php",
      "line": 100,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Author"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ],
            "cache_key": [
              "boolean",
              false
            ],
            "circular_reference_limit": [
              "array",
              {
                "00000000396b5a3f0000000043f65eba": [
                  "integer",
                  1
                ]
              }
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Serializer",
      "short_class": "AbstractItemNormalizer",
      "class": "ApiPlatform\\Core\\Serializer\\AbstractItemNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\JsonLd\\Serializer\\ItemNormalizer.php",
      "line": 73,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Author"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ],
            "api_normalize": [
              "boolean",
              true
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\JsonLd\\Serializer",
      "short_class": "ItemNormalizer",
      "class": "ApiPlatform\\Core\\JsonLd\\Serializer\\ItemNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Serializer.php",
      "line": 133,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Author"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ],
            "iri": [
              "string",
              "/api/authors/1"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer",
      "short_class": "Serializer",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\CollectionNormalizer.php",
      "line": 89,
      "args": [
        [
          "object",
          "App\\Entity\\Library\\Author"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "CollectionNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\CollectionNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\PartialCollectionViewNormalizer.php",
      "line": 49,
      "args": [
        [
          "object",
          "ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Paginator"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ],
            "jsonld_has_context": [
              "boolean",
              true
            ],
            "api_sub_level": [
              "boolean",
              true
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "PartialCollectionViewNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\PartialCollectionViewNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\Hydra\\Serializer\\CollectionFiltersNormalizer.php",
      "line": 65,
      "args": [
        [
          "object",
          "ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Paginator"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Hydra\\Serializer",
      "short_class": "CollectionFiltersNormalizer",
      "class": "ApiPlatform\\Core\\Hydra\\Serializer\\CollectionFiltersNormalizer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Serializer.php",
      "line": 133,
      "args": [
        [
          "object",
          "ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Paginator"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer",
      "short_class": "Serializer",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->",
      "function": "normalize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\serializer\\Serializer.php",
      "line": 106,
      "args": [
        [
          "object",
          "ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Paginator"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\Serializer",
      "short_class": "Serializer",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->",
      "function": "serialize",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php",
      "line": 69,
      "args": [
        [
          "object",
          "ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Paginator"
        ],
        [
          "string",
          "jsonld"
        ],
        [
          "array",
          {
            "collection_operation_name": [
              "string",
              "get"
            ],
            "operation_type": [
              "string",
              "collection"
            ],
            "resource_class": [
              "string",
              "App\\Entity\\Library\\Author"
            ],
            "request_uri": [
              "string",
              "/api/authors"
            ],
            "uri": [
              "string",
              "http://localhost/api/authors"
            ],
            "resources": [
              "object",
              "class@anonymous\u0000D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\api-platform\\core\\src\\EventListener\\SerializeListener.php0000022DE7FC07D9"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\EventListener",
      "short_class": "SerializeListener",
      "class": "ApiPlatform\\Core\\EventListener\\SerializeListener",
      "type": "->",
      "function": "onKernelView",
      "file": null,
      "line": null,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
        ],
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
        ]
      ]
    },
    {
      "namespace": "",
      "short_class": "",
      "class": "",
      "type": "",
      "function": "call_user_func",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\event-dispatcher\\Debug\\WrappedListener.php",
      "line": 104,
      "args": [
        [
          "array",
          [
            [
              "object",
              "ApiPlatform\\Core\\EventListener\\SerializeListener"
            ],
            [
              "string",
              "onKernelView"
            ]
          ]
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
        ],
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher\\Debug",
      "short_class": "WrappedListener",
      "class": "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener",
      "type": "->",
      "function": "__invoke",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\event-dispatcher\\EventDispatcher.php",
      "line": 212,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
        ],
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\EventDispatcher\\EventDispatcher"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher",
      "short_class": "EventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
      "type": "->",
      "function": "doDispatch",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\event-dispatcher\\EventDispatcher.php",
      "line": 44,
      "args": [
        [
          "array",
          [
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ],
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ],
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ],
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ],
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ]
          ]
        ],
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher",
      "short_class": "EventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
      "type": "->",
      "function": "dispatch",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\event-dispatcher\\Debug\\TraceableEventDispatcher.php",
      "line": 139,
      "args": [
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher\\Debug",
      "short_class": "TraceableEventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher",
      "type": "->",
      "function": "dispatch",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\http-kernel\\HttpKernel.php",
      "line": 156,
      "args": [
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "HttpKernel",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "->",
      "function": "handleRaw",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\http-kernel\\HttpKernel.php",
      "line": 66,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ],
        [
          "integer",
          1
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "HttpKernel",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "->",
      "function": "handle",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\vendor\\symfony\\http-kernel\\Kernel.php",
      "line": 190,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ],
        [
          "integer",
          1
        ],
        [
          "boolean",
          true
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "Kernel",
      "class": "Symfony\\Component\\HttpKernel\\Kernel",
      "type": "->",
      "function": "handle",
      "file": "D:\\dev\\php\\projects\\php-sf-flex-webpack-encore-vuejs\\public\\index.php",
      "line": 25,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ]
      ]
    }
  ]
}

JS app and CSRF: Guard may not be the best solution

I wrote the CSRFTokenAuthenticator just to add a csrf token check on all non GET route

But the guard also does the authentication whereas by default it's allready done by Json login security.

So in fact i need an event called only when on firewall security_json and on non GET call.
This event would only check token validity and would not take care the fact that it's on login route or not.

How to do this ?

Login JS : find a way to remove the isLoggedIn flag from localStorage when an API call return 403

For instance, when the user log in successfully, then a flag is set in localStorage, but no mecanims exists to remove it when a future API call return 403

Maybe the pattern is not good: should we keep information in localStorage ?
A user come into the app, if we don't use the localStorage then we have to do an HTTP call to check if he is connected (with its cookie we can bind the PHPSessId, and then identify the user with the session). If we use the localStorage we don't have to do the call but, we might have an error with the first next API call because in fact the user was not authentified.

Add a way to keep the user authentified when you do a refresh on a vuejs page

For instance : a user goes to /demo/books
then it's redirected to /demo/books#/login (or something like this)
then you fill the form and validate the form
if it's ok you are redirected on the todos list (to be finished)

but nothing is stored anywhere to check if the user is already loggedIn => on server side we should store if a user is logged (how to do this with Sf2 and json-login ?) Then, the vue-router guards should do an http-call and store response in cache for X minutes (use PouchDB for this ?) if delay is over do the call again. If an error HTTP (403...) happen then clear the cache...

Investigate on this feature

install instructions

i install this project with
composer create-project symfony/skeleton sf-flex-encore-vuejs

try run
npm run init-project

and get

:~/PhpstormProjects/sf-flex-encore-vuejs$ npm run init-project
npm ERR! missing script: init-project

What I miss?

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.