Symfony advanced example

Overview

In the Symfony basic example, we learned how to create a fully working basic Api. In this chapter we will enrich it to highlight the main Code-bay code generation features. For demonstration purposes, we are going to add two operations to our Festival Api to store and retrieve festival events from the session.

Create a new path

In order to create festival events we need to add a new path and activate its post method. Go to the festival details page and click on the Paths link in the side menu. On this page your are able to create a new path, proceed and enter /festival in the path field name as shown below. Click on the cog icon of the newly created path to access its details page. You can see that none of the available method is active, click on the POST card actions icon and select Add to display the operation creation form :

When creating a new operation you must enter a unique operation id in the scope of the Api and a tag. This information will be used later to generate the code, you can get more details about how the code is generated in the dedicated docs. To keep things simple here let's enter postFestival as operation id and Festival for the tag.

Posting festival objects

At this stage we created our /festival path and activated its post method. You can run a generation process in your IDE to check that a new src/Api/Action/Festival/PostFestival/Stages/PostFestivalBusinessStage.phpfile has been created. This is great but this class does not do anything yet. Before adding some code logic, we will focus on the input parameters. Indeed we did not define our Festival creation payload which should be an object with three properties :

  • name : the name of our festival event
  • contact : an email address contact
  • capacity : maximum number of people

Let's add a parameter to our operation by clicking on the Parameters tab. Enter body as parameter name and click on the Add button :

We are now ready to define the object properties as listed above, click on the Define schema button, a modal will pop to edit the parameter's shape. Begin with selecting Object in the type selector field and add the three properties name, contact and capacity. The resulting json view on the left side of the screen should look like the image below.

All the properties are defined but they are all defined as string. To make sure our backend data does not get polluted by wrong user input, we are going to add some parameters on these object properties.

Handle validation

There are two levels of data validation in Code-bay. The first one is based on the Swagger definitions format although the second one is tackled through specific stages and requires the application context. Let's focus on the format first, we want our festival object to respect the following rules :

  • The name and capacity properties must exist
  • Name should be a string
  • Capacity should be a non negative number

All these rules checks will be handled by auto generated code, we just have to set up the appropriate parameters for our definition. For the first rule click on the name and capacity chips and click on the Required switch. Go back to object level by clicking on the Home icon as shown below

Click on the name chip and set a minimum length of 1, you can save the definition by using Ctrl+S shortcut. Finish by defining a minimum value of 0 for the capacity, the full object now looks like this :

Great, everything is set up correctly but it would be better if this body parameter could be stored as a referenced definition in our Api so that it could be reused somewhere else. A button at the right bottom of the page allows you to do it directly from the parameter form. Click it and select the Save as definition option. A form will popup asking you for the new definition name, enter this name as illustrated below :

Once confirmed, a new definition is created and accessible in the definitions page, you can access and modify it from there. It is now time to test the format validation (don't forget to generate the code again from your IDE), in this tutorial we are using Postman to test web services but you can use any rest client. So let's try to post an invalid payload and check that the backend returns an error :

In this example, we provided an empty string although its minimum length is 1 which makes the auto generated format stage to throw an exception. Format validation is really useful but it cannot handle every validation aspect of an application. For instance for this Api we don't want a user to be able to create a festival having a name that already exists. For this use case we will create a specific validation stage.

Set up a custom stage

Navigate to the Stages section and create a new stage by clicking on the Add new stage button.

In the popup that appears, enter validation for the stage name and select the Quick create new serviceoption. You can let the service key, full class path and variable name unchanged, the default values will work out of the box. This will automatically create the stage service for you. Click on the Create button and submit the form.

The new stage is now visible in the list but is located at the bottom.

This means that it will be the last executed stage in the Code-bay request pipeline although we want the validation to happen first. Simply drag and drop the stage at the top of the pipeline to fix it.

Write business logic

Before going through the details of the new validation stage, let's implement the login in the business stage. As said before we are saving festival items posted by user in the session. Of course in a real app we would save it in a database but the objective here is only to understand the Code-bay key concepts. Symfony provides a built-in class for manipulating the session but this service is not accessible directly in our business stage. Hopefully you can define stage dependencies which links to Code-bay services. All we have to do is to create a Service bound to the built-in Symfony Session class. Creating this service is really simple, on the side menu, click on the Services link and create a new service as below :

It is now possible to add the session service as a dependency is our stages. Let's add it to our business and validation stages :

Regenerate the code in you IDE, open the src/Api/Action/Festival/PostFestival/Stages/PostFestivalBusinessStage.php and add the following code :

        // Retrieve existing festivals
        // Note that getSession method has been automatically generated by Code-bay thanks to dependencies
        $savedFestival = $this->getSession()->get('festivals', []);

        // Get new festival values
        $body = $context->getMessage()->getBody();

        // Save it to session
        $savedFestival[$body->getName()] = $body->toArray();
        $this->getSession()->set('festivals', $savedFestival);

Go back to Postman and submit a payload with correct values, this will save the festival to the session. Now we can create a new service to request existing festivals.

Get saved festival objects

In the same manner that we activated the post method for our /festival path, let's add an operation for the get method. To do so the /festival path detail page and click on the get method add action. Enter getFestivals in operation id field and Festival for the tag.

Generate the code again and check that the file src/Api/Action/Festival/GetFestivals/Stages/GetFestivalsBusinessStage.php has been created. Before adding the code in the business stage, lets update the response for response code 200. Go to the Responses tab and select the response associated with HTTP code 200. This response has been created by default when activating the GET method but its schema is undefined. In our case we want to return an array of Festival objects where each of theme has a name, contact and capacity property.

In the schema form select Array for the type and click on the items breadcrumb element on the top of the screen :

We could create a new object a save it as a definition like we did for the Festival creation payload but to keep things simple here we are going to reuse our CreateFestivalPayload definition. Select Reference in the type selector field and search for CreateFestivalPayload :

Once the response schema saved, go to the operation stages tab and add the sf_session service to the business stage dependencies. Launch a code generation again and type the following code in the body of the processStage method of file src/Api/Action/Festival/GetFestivals/Stages/GetFestivalsBusinessStage.php

        // Retrieve existing festivals
        $savedFestival = $this->getSession()->get('festivals', []);
        $responseView = [];
        foreach ($savedFestival as $festivalName => $festival) {
            // Generated\Api\Definition\CreateFestivalPayload
            $currentFestival = new CreateFestivalPayload();
            $currentFestival->setName($festival['name']);
            $currentFestival->setContact($festival['contact'] ?? '');
            $currentFestival->setCapacity($festival['capacity']);
            $responseView[] = $currentFestival;
        }
        $context->setResponseCode200View($responseView);

After posting a few festival items via Postman, navigate to /festival, you should see them appear in your browser as a Json array. We are almost done, the last element we have to set up is our validation stage that we didn't implement yet. Open file src/Api/Action/Festival/PostFestival/Stages/PostFestivalValidationStage.php and write the following code :

        // Get new festival values
        $body = $context->getMessage()->getBody();

        // Retrieve existing festivals
        $savedFestival = $this->getSession()->get('festivals', []);

        // Check festival name does not already exist
        if (array_key_exists($body->getName(), $savedFestival)) {
            throw new \Exception(sprintf('Festival %s already exists', $body->getName()));
        }

If you try to post two festival objets with the same name, an exception will be throw stopping the pipeline at this validation stage. The default error exception stage only handle ErrorViewException and ApiFormatException, from sf5.2-core package, any other exception type will result in producing a HTTP 500 error response. To change this behavior, you can either create a custom exception handler service that will extend the one provided in Code-bay core package, define it as a service in your api and bind it to your pipeline in the stages tab :

or throw a ErrorViewException and define a new response with the desired HTTP response code. We will illustrate the second solution by creating a 400 HTTP code response with a simple error definition as schema. In order to create it go back to the /festival post method and click on the Responses tab. Create a new response with code 400 and define its schema as follows :

As you can see we just created a basic object with only one name field. Save it as a definition named ApiError and modify the code inside src/Api/Action/Festival/PostFestival/Stages/PostFestivalValidationStage.php as below :

        // Get new festival values
        $body = $context->getMessage()->getBody();

        // Retrieve existing festivals
        $savedFestival = $this->getSession()->get('festivals', []);

        // Check festival name does not already exist
        if (array_key_exists($body->getName(), $savedFestival)) {
            // Generated\Api\Definition\ApiError
            $error = new ApiError();
            $error->setMessage(sprintf('Festival %s already exists', $body->getName()));
            $context->setResponseCode400View($error);
            throw new ErrorViewException(null, 'ERROR_ID', 400);
        }

Try to create two festival with same name again, the response is now mapped as an ApiError object you defined in the response schema :


© Code-bay 2021, All rights reserved

When you visit or interact with our sites, services or tools, we use cookies for storing information to help provide you with a better, faster and safer experience. None of these cookies are used for marketing purposes.