Symfony Application Using Routing
Sample of how to use routing in a Symfony web application.
The main meaning of these sample is in the configuration and routing settings, and not in the business logic, so the actions in the controllers have pseudo-code as comments.
The API describes features of a product catalog in an online store.
List of entities that are used in this API:
- Category
- Product
- Review
Entities don't really exist, only controllers and actions are designed.
The routing configuration for the controllers:
# /config/routes.yaml
controllers:
resource: ../src/Controller/Catalog
type: attribute
prefix: '/catalog'
name_prefix: 'catalog_'
trailing_slash_on_root: false
This means that the routes for all controllers in the src/Controller/Catalog directory will be prefixed with /catalog, so this prefix can be omitted in the controllers. The /catalog prefix is needed to separate the catalog section with products from other sections of the site such as /blog, /news, /docs, /contacts, /about-us.
List of available routes:
symfony console debug:router
--------------------------- -------- -------- ------ ----------------------------
Name Method Scheme Host Path
--------------------------- -------- -------- ------ ----------------------------
_preview_error ANY ANY ANY /_error/{code}.{_format}
catalog_categories_list GET ANY ANY /catalog/categories
catalog_categories_item GET ANY ANY /catalog/categories/{uuid}
catalog_categories_add POST ANY ANY /catalog/categories
catalog_categories_edit PUT ANY ANY /catalog/categories/{uuid}
catalog_categories_delete DELETE ANY ANY /catalog/categories/{uuid}
catalog_products_list GET ANY ANY /catalog/products
catalog_products_item GET ANY ANY /catalog/products/{uuid}
catalog_products_add POST ANY ANY /catalog/products
catalog_products_edit PUT ANY ANY /catalog/products/{uuid}
catalog_products_delete DELETE ANY ANY /catalog/products/{uuid}
catalog_reviews_list GET ANY ANY /catalog/reviews
catalog_reviews_item GET ANY ANY /catalog/reviews/{uuid}
catalog_reviews_add POST ANY ANY /catalog/reviews
catalog_reviews_edit PUT ANY ANY /catalog/reviews/{uuid}
catalog_reviews_delete DELETE ANY ANY /catalog/reviews/{uuid}
--------------------------- -------- -------- ------ ----------------------------
Category Controller
// src/Controller/Catalog/CategoryController.php
#[Route('/categories', name: 'categories_')]
class CategoryController extends AbstractController
{
/**
* Return list of categories
*
* Matches method and path: GET /catalog/categories
*/
#[Route(
'',
'list',
methods: ['GET']
)]
public function list(): JsonResponse
{
// make request to Category repository
// if categories is empty return empty array
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Return single Category
*
* Matches method and path: GET /catalog/categories/7ea27a43-b587-4baa-83c1-aa4ee52db2a1
*/
#[Route(
'/{uuid}',
'item',
requirements: ['uuid' => Requirement::UUID],
methods: ['GET']
)]
public function item(string $uuid): JsonResponse
{
// make find request to Category repository
// if category not found return HTTP 404 (Not Found)
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Add new Category
*
* Matches method and path: POST /catalog/categories
*/
#[Route(
'',
'add',
methods: ['POST'],
condition: "service('app.routing.condition.checker').isRequestBodyNotEmpty(request)"
)]
public function add(Request $request): JsonResponse
{
// deserialize data from Request to Category object
// validate Category object data (if validation fail return error message with status HTTP 400 (Bad Request))
// persist to database
// return HTTP 201 (Created)
return $this->json([]);
}
/**
* Edit Category
*
* Matches method and path: PUT /catalog/categories/16793782-d36c-4b6a-97d4-2e6c1fba8782
*/
#[Route(
'/{uuid}',
'edit',
requirements: ['uuid' => Requirement::UUID],
methods: ['PUT'],
condition: "service('app.routing.condition.checker').isRequestBodyNotEmpty(request)"
)]
public function edit(string $uuid, Request $request): JsonResponse
{
// make find request to Category repository
// if category not found return HTTP 404 (Not Found)
// deserialize data from Request and populate to exist Category object
// validate Category object data (if validation fail return error message with status HTTP 400 (Bad Request))
// persist edited Category object to database
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Delete Category
*
* Matches method and path: DELETE /catalog/categories/2a54a70a-0765-4d0d-99b3-b57a30d3ed67
*/
#[Route(
'/{uuid}',
'delete',
requirements: ['uuid' => Requirement::UUID],
methods: ['DELETE']
)]
public function delete(string $uuid): JsonResponse
{
// make find request to Category repository
// if category not found return HTTP 404 (Not Found)
// delete Category from database
// return HTTP 200 (OK)
return $this->json([]);
}
}
Product Controller
// src/Controller/Catalog/ProductController.php
#[Route('/products', 'products_')]
class ProductController extends AbstractController
{
/**
* Return list of products
*
* Matches method and path: GET /catalog/products
*
* Returns a list of filtered products by "jeans" category
* Matches method and path: GET /catalog/products?categories[]=jeans
*
* Returns a list of found products by word "nike" in the title, description or characteristic of the product
* Matches method and path: GET /catalog/products?search=nike
*/
#[Route(
'',
'list',
methods: ['GET']
)]
public function list(Request $request): JsonResponse
{
// make get/filter/search request to Product repository
// if products is empty return empty array
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Return single Product
*
* Matches method and path: GET /catalog/products/756ab18c-31a1-4981-b8d0-67eb8f195e80
*/
#[Route(
'/{uuid}',
'item',
requirements: ['uuid' => Requirement::UUID],
methods: ['GET']
)]
public function item(string $uuid): JsonResponse
{
// make find request to Product repository
// if Product not found return HTTP 404 (Not Found)
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Add new Product
*
* Matches method and path: POST /catalog/products
*/
#[Route(
'',
'add',
methods: ['POST'],
condition: "service('app.routing.condition.checker').isRequestBodyNotEmpty(request)"
)]
public function add(Request $request): JsonResponse
{
// deserialize data from Request to Product object
// validate Product object data (if validation fail return error message with status HTTP 400 (Bad Request))
// persist to database
// return HTTP 201 (Created)
return $this->json([]);
}
/**
* Edit Product
*
* Matches method and path: PUT /catalog/products/3f3652c1-990a-4b5c-bb36-0222df2b09e1
*/
#[Route(
'/{uuid}',
'edit',
requirements: ['uuid' => Requirement::UUID],
methods: ['PUT'],
condition: "service('app.routing.condition.checker').isRequestBodyNotEmpty(request)"
)]
public function edit(string $uuid, Request $request): JsonResponse
{
// make find request to Product repository
// if Product not found return HTTP 404 (Not Found)
// deserialize data from Request and populate to exist Product object
// validate Product object data (if validation fail return error message with status HTTP 400 (Bad Request))
// persist edited Product object to database
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Delete Product
*
* Matches method and path: DELETE /catalog/products/a3494f19-5079-4841-854e-63416dd54de5
*/
#[Route(
'/{uuid}',
'delete',
requirements: ['uuid' => Requirement::UUID],
methods: ['DELETE']
)]
public function delete(string $uuid): JsonResponse
{
// make find request to Product repository
// if Product not found return HTTP 404 (Not Found)
// delete Product from database
// return HTTP 200 (OK)
return $this->json([]);
}
}
Review Controller
// src/Controller/Catalog/ReviewController.php
#[Route('/reviews', 'reviews_')]
class ReviewController extends AbstractController
{
/**
* Return list of review
*
* Matches method and path: GET /catalog/reviews
*
* Returns a list of reviews for the product with UUID "cdedec98-d702-422d-9e34-dc624990331c"
* Matches method and path: GET /catalog/reviews?product=cdedec98-d702-422d-9e34-dc624990331c
*/
#[Route(
'',
'list',
methods: ['GET']
)]
public function list(): JsonResponse
{
// make get/filter request to Review repository
// if reviews is empty return empty array
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Return single Review
*
* Matches method and path: GET /catalog/reviews/71ca5f0a-7e45-44e5-85fe-252d25ffb45e
*/
#[Route(
'/{uuid}',
'item',
requirements: ['uuid' => Requirement::UUID],
methods: ['GET']
)]
public function item(string $uuid): JsonResponse
{
// make find request to Review repository
// if Review not found return HTTP 404 (Not Found)
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Add new Review
*
* Matches method and path: POST /catalog/reviews
*/
#[Route(
'',
'add',
methods: ['POST'],
condition: "service('app.routing.condition.checker').isRequestBodyNotEmpty(request)"
)]
public function add(Request $request): JsonResponse
{
// deserialize data from Request to Review object
// validate Review object data (if validation fail return error message with status HTTP 400 (Bad Request))
// persist to database
// return HTTP 201 (Created)
return $this->json([]);
}
/**
* Edit Review
*
* Matches method and path: PUT /catalog/reviews/6300fae7-645f-4f1a-a558-fb68f7a00ec2
*/
#[Route(
'/{uuid}',
'edit',
requirements: ['uuid' => Requirement::UUID],
methods: ['PUT'],
condition: "service('app.routing.condition.checker').isRequestBodyNotEmpty(request)"
)]
public function edit(string $uuid, Request $request): JsonResponse
{
// make find request to Review repository
// if Review not found return HTTP 404 (Not Found)
// deserialize data from Request and populate to exist Review object
// validate Review object data (if validation fail return error message with status HTTP 400 (Bad Request))
// persist edited Review object to database
// return HTTP 200 (OK)
return $this->json([]);
}
/**
* Delete Review
*
* Matches method and path: DELETE /catalog/reviews/ebd7b29c-d900-4f70-a5e1-0dc8e547bc0d
*/
#[Route(
'/{uuid}',
'delete',
requirements: ['uuid' => Requirement::UUID],
methods: ['DELETE']
)]
public function delete(string $uuid): JsonResponse
{
// make find request to Review repository
// if Review not found return HTTP 404 (Not Found)
// delete Review from database
// return HTTP 200 (OK)
return $this->json([]);
}
}
Resources
Source on GitHub: Symfony Application Using Routing
Symfony documentation: Routing