From 1c8318db18dc2edf9d239661d225f8e410e0bb2f Mon Sep 17 00:00:00 2001 From: Karol Fiturski Date: Wed, 27 Jul 2016 22:17:12 +0200 Subject: [PATCH 1/3] Added simple task crud tutorial --- Resources/doc/index.rst | 7 + Resources/doc/task-crud-tutorial.rst | 336 +++++++++++++++++++++++++++ 2 files changed, 343 insertions(+) create mode 100644 Resources/doc/task-crud-tutorial.rst diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst index bd7a02e25..ace93aebf 100644 --- a/Resources/doc/index.rst +++ b/Resources/doc/index.rst @@ -53,6 +53,13 @@ Config reference - :doc:`Annotations reference ` for a reference on the available configurations through annotations +Task CRUD tutorial +-------------------- + +The following tutorial shows how to handle forms in the "REST" like way + +- :doc:`Task CRUD tutorial ` + Example applications -------------------- diff --git a/Resources/doc/task-crud-tutorial.rst b/Resources/doc/task-crud-tutorial.rst new file mode 100644 index 000000000..bc2b8f041 --- /dev/null +++ b/Resources/doc/task-crud-tutorial.rst @@ -0,0 +1,336 @@ +Task CRUD tutorial +================== + +Suppose that we have a fresh Symfony project with FOSRestBundle installed and configured in that way: + +.. code-block:: yml + + // app/config/config.yml + fos_rest: + view: + view_response_listener: force # for always return View from FOSRestBundle Annotations + formats: + json: true + body_listener: true # for decoding our request to forms + routing_loader: + default_format: json + include_format: false # for remove ".json" suffix from all of our routes + +A) Create Task entity +--------------------- + +For this tutorial we create Task entity with two simple fields ``name`` and ``completed`` + +.. code-block:: php + + // src/AppBundle/Entity/Task.php + namespace AppBundle\Entity; + + use Doctrine\ORM\Mapping as ORM; + + /** + * Task + * + * @ORM\Table(name="task") + */ + class Task + { + /** + * @var int + * + * @ORM\Column(name="id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @var string + * + * @ORM\Column(name="name", type="string", length=255) + */ + private $name; + + /** + * @var boolean + * + * @ORM\Column(name="completed", type="boolean") + */ + private $completed; + + + /** + * Get id + * + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * Set name + * + * @param string $name + * + * @return Task + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set code + * + * @param string $completed + * + * @return Task + */ + public function setCompleted($completed) + { + $this->completed = $completed; + + return $this; + } + + /** + * Get code + * + * @return string + */ + public function isCompleted() + { + return $this->completed; + } + } + +B) Create TaskType form +----------------------- + +For handling data comes from request we need to create our ``TaskType.php`` file with standard content +(pleas note that we setup ``getName()`` method with ``'task'`` and ``'csrf_protection => false'`` - this is +important for creating requests which we will do later): + +.. code-block:: php + + // src/AppBundle/Form/TaskType.php + namespace AppBundle\Form; + + use Symfony\Component\Form\AbstractType; + use Symfony\Component\Form\FormBuilderInterface; + use Symfony\Component\OptionsResolver\OptionsResolver; + + class TaskType extends AbstractType + { + /** + * @param FormBuilderInterface $builder + * @param array $options + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('name') + ->add('completed') + ; + } + + /** + * @param OptionsResolver $resolver + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'AppBundle\Entity\Task', + 'csrf_protection' => false, + )); + } + + /** + * @return string + */ + public function getName() + { + return 'task'; + } + } + +C) Create TaskController +------------------------ + +For expose our REST API methods (routes) lets add the following controller: + +.. code-block:: php + + // src/AppBundle/Controller/TaskController.php + namespace AppBundle\Controller; + + use AppBundle\Entity\Task; + use AppBundle\Form\TaskType; + use FOS\RestBundle\Controller\Annotations\View; + use FOS\RestBundle\Controller\FOSRestController; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + + class TaskController extends FOSRestController + { + /** + * List all tasks + * + * @View + */ + public function getTasksAction() + { + return $this->getDoctrine()->getRepository('AppBundle:Task')->findAll(); + } + + /** + * Show specific task + * + * @View + */ + public function getTaskAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + $task = $em->getRepository('AppBundle:Task')->find($id); + + if (!$task) { + throw $this->createNotFoundException(); + } + + return $task; + } + + /** + * Create new task + * + * @View + */ + public function postTasksAction(Request $request) + { + $task = new Task(); + $form = $this->createForm(TaskType::class, $task); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($task); + $em->flush(); + + return $task; + } + + throw new BadRequestHttpException(); + } + + /** + * Update existing task + * + * @View + */ + public function putTasksAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + $task = $em->getRepository('AppBundle:Task')->find($id); + if (!$task) { + throw $this->createNotFoundException(); + } + $form = $this->createForm(TaskType::class, $task, [ + 'method' => 'PUT' + ]); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em->persist($task); + $em->flush(); + + return $task; + } + + throw new BadRequestHttpException(); + } + + /** + * Delete existing task + * + * @View + */ + public function deleteTasksAction(Request $request, $id) + { + $em = $this->getDoctrine()->getManager(); + $task = $em->getRepository('AppBundle:Task')->find($id); + if (!$task) { + throw $this->createNotFoundException(); + } + + $em->remove($task); + $em->flush(); + } + } + +D) Update routing.yml +--------------------- + +For expose our REST API methods (routes) lets add the following controller: + +.. code-block:: yml + + // app/config/routing.yml + tasks: + type: rest + resource: AppBundle\Controller\TaskController + +E) Create database +------------------ + +We created our entity so we have to create database and schema: + +.. code-block:: bash + + $ bin/console doctrine:database:create + $ bin/console doctrine:schema:create + +F) Test our API! +---------------- + +After setup our application it's time to test our REST API, so lets run the Symfony built in server: + +.. code-block:: bash + + $ bin/console server:run + +and test our endpoints with ``curl`` or I recommend Postman google-chrome extension: + +.. code-block:: bash + + # get list of tasks + $ curl -X GET -H 'Content-Type: application/json' http://localhost:8000/tasks + + # create new task + $ curl -X POST -H 'Content-Type: application/json' -d '{ "task": { "name": "name of the task", "completed": false } }' http://localhost:8000/tasks + + # show task + $ curl -X GET -H 'Content-Type: application/json' http://localhost:8000/tasks/1 + + # update existing task + $ curl -X PUT -H 'Content-Type: application/json' -d '{ "task": { "name": "new name of the task", "completed": true } }' http://localhost:8000/tasks/1 + + # delete task + $ curl -X DELETE -H 'Content-Type: application/json' http://localhost:8000/tasks/1 + +That was it! From ae687da1763d637700351c231b12331406fd9331 Mon Sep 17 00:00:00 2001 From: Karol Fiturski Date: Wed, 27 Jul 2016 22:32:59 +0200 Subject: [PATCH 2/3] Fix D) Update routing section --- Resources/doc/task-crud-tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/doc/task-crud-tutorial.rst b/Resources/doc/task-crud-tutorial.rst index bc2b8f041..600e379e5 100644 --- a/Resources/doc/task-crud-tutorial.rst +++ b/Resources/doc/task-crud-tutorial.rst @@ -286,7 +286,7 @@ For expose our REST API methods (routes) lets add the following controller: D) Update routing.yml --------------------- -For expose our REST API methods (routes) lets add the following controller: +Lte's update routing configuration: .. code-block:: yml From bf5e8adaa070be1dddb5e6a4d15640b595a1aea6 Mon Sep 17 00:00:00 2001 From: Karol Fiturski Date: Thu, 27 Oct 2016 23:44:48 +0200 Subject: [PATCH 3/3] Improvements from comments --- Resources/doc/task-crud-tutorial.rst | 97 ++++++---------------------- 1 file changed, 19 insertions(+), 78 deletions(-) diff --git a/Resources/doc/task-crud-tutorial.rst b/Resources/doc/task-crud-tutorial.rst index 600e379e5..1a8e09d3a 100644 --- a/Resources/doc/task-crud-tutorial.rst +++ b/Resources/doc/task-crud-tutorial.rst @@ -3,7 +3,7 @@ Task CRUD tutorial Suppose that we have a fresh Symfony project with FOSRestBundle installed and configured in that way: -.. code-block:: yml +.. code-block:: yaml // app/config/config.yml fos_rest: @@ -19,7 +19,7 @@ Suppose that we have a fresh Symfony project with FOSRestBundle installed and co A) Create Task entity --------------------- -For this tutorial we create Task entity with two simple fields ``name`` and ``completed`` +For this tutorial we create ``Task`` entity with two simple fields ``name`` and ``completed`` .. code-block:: php @@ -29,15 +29,11 @@ For this tutorial we create Task entity with two simple fields ``name`` and ``co use Doctrine\ORM\Mapping as ORM; /** - * Task - * * @ORM\Table(name="task") */ class Task { /** - * @var int - * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") @@ -45,37 +41,20 @@ For this tutorial we create Task entity with two simple fields ``name`` and ``co private $id; /** - * @var string - * * @ORM\Column(name="name", type="string", length=255) */ private $name; /** - * @var boolean - * * @ORM\Column(name="completed", type="boolean") */ private $completed; - - /** - * Get id - * - * @return int - */ public function getId() { return $this->id; } - /** - * Set name - * - * @param string $name - * - * @return Task - */ public function setName($name) { $this->name = $name; @@ -83,23 +62,11 @@ For this tutorial we create Task entity with two simple fields ``name`` and ``co return $this; } - /** - * Get name - * - * @return string - */ public function getName() { return $this->name; } - /** - * Set code - * - * @param string $completed - * - * @return Task - */ public function setCompleted($completed) { $this->completed = $completed; @@ -107,11 +74,6 @@ For this tutorial we create Task entity with two simple fields ``name`` and ``co return $this; } - /** - * Get code - * - * @return string - */ public function isCompleted() { return $this->completed; @@ -121,8 +83,8 @@ For this tutorial we create Task entity with two simple fields ``name`` and ``co B) Create TaskType form ----------------------- -For handling data comes from request we need to create our ``TaskType.php`` file with standard content -(pleas note that we setup ``getName()`` method with ``'task'`` and ``'csrf_protection => false'`` - this is +For handling data coming from the request we need to create our ``TaskType.php`` file with standard content +(please note that we set up ``getName()`` method with ``'task'`` and ``'csrf_protection => false'`` - this is important for creating requests which we will do later): .. code-block:: php @@ -136,10 +98,6 @@ important for creating requests which we will do later): class TaskType extends AbstractType { - /** - * @param FormBuilderInterface $builder - * @param array $options - */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder @@ -148,9 +106,6 @@ important for creating requests which we will do later): ; } - /** - * @param OptionsResolver $resolver - */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( @@ -158,14 +113,6 @@ important for creating requests which we will do later): 'csrf_protection' => false, )); } - - /** - * @return string - */ - public function getName() - { - return 'task'; - } } C) Create TaskController @@ -188,24 +135,19 @@ For expose our REST API methods (routes) lets add the following controller: class TaskController extends FOSRestController { /** - * List all tasks - * * @View */ public function getTasksAction() { - return $this->getDoctrine()->getRepository('AppBundle:Task')->findAll(); + return $this->getRepository()->findAll(); } /** - * Show specific task - * * @View */ public function getTaskAction(Request $request, $id) { - $em = $this->getDoctrine()->getManager(); - $task = $em->getRepository('AppBundle:Task')->find($id); + $task = $this->getRepository()->find($id); if (!$task) { throw $this->createNotFoundException(); @@ -215,8 +157,6 @@ For expose our REST API methods (routes) lets add the following controller: } /** - * Create new task - * * @View */ public function postTasksAction(Request $request) @@ -234,18 +174,15 @@ For expose our REST API methods (routes) lets add the following controller: return $task; } - throw new BadRequestHttpException(); + return $form; } /** - * Update existing task - * * @View */ public function putTasksAction(Request $request, $id) { - $em = $this->getDoctrine()->getManager(); - $task = $em->getRepository('AppBundle:Task')->find($id); + $task = $this->getRepository()->find($id); if (!$task) { throw $this->createNotFoundException(); } @@ -256,18 +193,17 @@ For expose our REST API methods (routes) lets add the following controller: $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { + $em = $this->getDoctrine()->getManager(); $em->persist($task); $em->flush(); return $task; } - throw new BadRequestHttpException(); + return $form; } /** - * Delete existing task - * * @View */ public function deleteTasksAction(Request $request, $id) @@ -281,6 +217,11 @@ For expose our REST API methods (routes) lets add the following controller: $em->remove($task); $em->flush(); } + + private function getRepository() + { + return $this->getDoctrine()->getRepository('AppBundle:Task'); + } } D) Update routing.yml @@ -300,7 +241,7 @@ E) Create database We created our entity so we have to create database and schema: -.. code-block:: bash +.. code-block:: terminal $ bin/console doctrine:database:create $ bin/console doctrine:schema:create @@ -308,15 +249,15 @@ We created our entity so we have to create database and schema: F) Test our API! ---------------- -After setup our application it's time to test our REST API, so lets run the Symfony built in server: +After having set up our application it's time to test our REST API, so lets run the Symfony built-in server: -.. code-block:: bash +.. code-block:: terminal $ bin/console server:run and test our endpoints with ``curl`` or I recommend Postman google-chrome extension: -.. code-block:: bash +.. code-block:: terminal # get list of tasks $ curl -X GET -H 'Content-Type: application/json' http://localhost:8000/tasks