{"id":196,"date":"2014-08-05T13:06:19","date_gmt":"2014-08-05T16:06:19","guid":{"rendered":"http:\/\/arielnavarrete.wordpress.com\/?p=196"},"modified":"2014-08-05T13:06:19","modified_gmt":"2014-08-05T16:06:19","slug":"knp_paginator-con-consulta-sql-personalizada-createnativequery","status":"publish","type":"post","link":"https:\/\/arielnavarrete.cl\/blog\/2014\/08\/05\/knp_paginator-con-consulta-sql-personalizada-createnativequery\/","title":{"rendered":"knp_paginator con consulta SQL personalizada (createNativeQuery)"},"content":{"rendered":"<p>El Bundle\u00a0<a title=\"KnpPaginator\" href=\"https:\/\/github.com\/KnpLabs\/KnpPaginatorBundle\" target=\"_blank\">KnpPaginator<\/a> es una excelente ayuda que nos permite paginar de una forma bastante amigable y facil, normalmente su uso se refiera a pasarle una query que formamos mediante createQuery y este la procesa y nos entrega el resultado paginado segun los parametros que definamos pero&#8230; \u00bfQue sucede cuando deseamos pasarle una consulta personalizada? la respuesta es tajante, no lo soporta, pero nos entrega herramientas para hacerlo.<\/p>\n<p><!--more Seguir leyendo...--><\/p>\n<p>En su documentaci\u00f3n hace referencia a <a title=\"Creating custom subscriber\" href=\"https:\/\/github.com\/KnpLabs\/KnpPaginatorBundle\/blob\/master\/Resources\/doc\/custom_pagination_subscribers.md\" target=\"_blank\">Creating custom subscriber<\/a>, esto especificamente nos permite personalizar la funcion que recibe los item de nuestra paginacion, siguiendo los mismos pasos dados en la documentaci\u00f3n tenemos<\/p>\n<p>1) Creamos el archivo <strong>PaginateDirectorySubscriber<\/strong> en la misma ruta indicada pero dentro de tu Bundle<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">&amp;lt;?php\n\n\/\/Ruta donde esta el archivo dentro de nuestro Bundle\nnamespace AN\\PruebaBundle\\Subscriber;\n\nuse Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;\nuse Knp\\Component\\Pager\\Event\\ItemsEvent;\nuse Doctrine\\ORM\\EntityManager;\nuse Doctrine\\ORM\\NativeQuery;\n\nclass PaginateDirectorySubscriber implements EventSubscriberInterface\n{\n\u00a0\u00a0 \u00a0protected $em;\n\n\u00a0\u00a0\u00a0 public function __construct(EntityManager $entityManager)\n\u00a0\u00a0\u00a0 {\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$this-&amp;gt;em = $entityManager;\n\u00a0\u00a0\u00a0 }\n\n\u00a0\u00a0\u00a0 public function items(ItemsEvent $event)\n\u00a0\u00a0\u00a0 {\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/Aqui ira nuestra logica a procesar\n\u00a0\u00a0\u00a0 }\n\n\u00a0\u00a0\u00a0 public static function getSubscribedEvents()\n\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return array(\n             \/*increased priority to override any internal*\/\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#039;knp_pager.items&#039; =&amp;gt; array(&#039;items&#039;, 1)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 );\n\u00a0\u00a0\u00a0 }\n}\n\n<\/pre>\n<p>Obviamente la documentaci\u00f3n nos indica que debemos agregar <strong>Finder<\/strong>, pero para el uso que deseamos darle no lo ocuparemos, asi que lo omitimos.<\/p>\n<p>Adem\u00e1s si te das cuenta agregue <strong>EntityManager<\/strong>, principalmente por si requieres hacer una consulta interna u otra cosa, para este ejemplo no lo ocuparemos, pero lo dejaremos incluido por si lo requieren.<\/p>\n<p>Por ultimo, agregue <strong>NativeQuery<\/strong> el cual nos ayudara a determinar si es una query normal o no.<\/p>\n<p>2) Creamos nuestro archivo <strong>paginate.xml<\/strong> como yo personalmente ocupo YAML, creare el archivo <strong>paginate.yml<\/strong>, la ruta donde ira sera similar a la que nos indican<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\n\n\/* Ruta donde esta este archivo :\n\nsymfony\\src\\AN\\PruebaBundle\\Resources\\config\\paginate.yml\n\n*\/\n\nservices:\n\u00a0\u00a0\u00a0 an_knp_paginator.subscriber:\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 class: AN\\PruebaBundle\\Subscriber\\PaginateDirectorySubscriber\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 scope: request\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 tags:\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 - { name: knp_paginator.subscriber }\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 arguments: &#x5B;@doctrine.orm.entity_manager]\n\n<\/pre>\n<p>Como dije anteriormente, le pasamos como arguments el <strong>@doctrine.orm.entity_manager<\/strong>, asi podremos ocupar nuestra <strong>EntityManager<\/strong> de requerirla.<\/p>\n<p>3) Agregamos a la configuraci\u00f3n nuestro archivo generado en el paso 2<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\n\n&amp;lt;?php\n\/\/Ruta donde esta\nnamespace AN\\PruebaBundle\\DependencyInjection;\n\nuse Symfony\\Component\\DependencyInjection\\ContainerBuilder;\nuse Symfony\\Component\\Config\\FileLocator;\nuse Symfony\\Component\\HttpKernel\\DependencyInjection\\Extension;\nuse Symfony\\Component\\DependencyInjection\\Loader;\n\n\/**\n\u00a0* This is the class that loads and manages your bundle configuration\n\u00a0*\n\u00a0* To learn more see {@link http:\/\/symfony.com\/doc\/current\/cookbook\/bundles\/extension.html}\n\u00a0*\/\nclass ANPruebaExtension extends Extension\n{\n\u00a0\u00a0\u00a0 \/**\n\u00a0\u00a0\u00a0\u00a0 * {@inheritDoc}\n\u00a0\u00a0\u00a0\u00a0 *\/\n\u00a0\u00a0\u00a0 public function load(array $configs, ContainerBuilder $container)\n\u00a0\u00a0\u00a0 {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $configuration = new Configuration();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $config = $this-&amp;gt;processConfiguration($configuration, $configs);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $loader = new Loader\\YamlFileLoader($container, new FileLocator(__DIR__.&#039;\/..\/Resources\/config&#039;));\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $loader-&amp;gt;load(&#039;services.yml&#039;);\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ Aqui agregamos nuestro archivo creado en el paso 2 para que lo cargue\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$loader-&amp;gt;load(&#039;paginate.yml&#039;);\n\u00a0\u00a0\u00a0 }\n}\n\n<\/pre>\n<p>4) Antes de ocuparlo, nos falta lo mas importante, agregar la logica que necesitamos en nuestro <strong>PaginateDirectorySubscriber<\/strong>, dentro de la funcion items agregamos lo siguiente, quedando asi<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\n\n\u00a0\u00a0\u00a0 public function items(ItemsEvent $event)\n\u00a0\u00a0\u00a0 {\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0 \/\/Si es una instancia de &amp;quot;NativeQuery&amp;quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ($event-&amp;gt;target instanceof NativeQuery)\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0{\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/Seteamos y asignamos\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$query = $event-&amp;gt;target;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $event-&amp;gt;count = count($query-&amp;gt;getArrayResult());\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $event-&amp;gt;items = array_slice(\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $query-&amp;gt;getArrayResult(),\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $event-&amp;gt;getOffset(),\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $event-&amp;gt;getLimit()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 );\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$event-&amp;gt;stopPropagation();\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n\u00a0\u00a0\u00a0 }\n\n<\/pre>\n<p>Revisemos:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">if ($event-&amp;gt;target instanceof NativeQuery)<\/pre>\n<p>Aqui preguntamos si es una instancia de <strong>NativeQuery<\/strong> o no, esto nos dira si fue creada con createNativeQuery o createQuery, lo cual nos permite distinguir entre una consulta creada normalmente con DQL (createQuery) y una personalizada (createNativeQuery), ya que si no agregamos esto, todas nuestra consultas normales pasaran por aqui y se producira un <strong>error<\/strong> ya que estas requieren otro tratamiento.<\/p>\n<p>5) Por \u00faltimo, lo llamamos desde nuestro Controller, aqu\u00ed asumimos que estamos creando una consulta SQL donde llamamos a Usuarios, esto obviamente lo puedes hacer con DQL pero es solo un ejemplo. Adem\u00e1s debemos guiarnos por la documentaci\u00f3n que nos entrega Doctrine sobre <a title=\"Native SQL\" href=\"http:\/\/doctrine-orm.readthedocs.org\/en\/latest\/reference\/native-sql.html\" target=\"_blank\">Native SQL<\/a><\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\n\/* Consulta SQL pura, fijandonos bien en los nombres de los\ncampos que estan en nuestra Base de Datos *\/\n$sql = &amp;quot;SELECT u.id,u.nombre FROM Usuario u&amp;quot;;\n<\/pre>\n<p>Ya tenemos nuestra consulta SQL, ahora debemos agregar <strong>ResultSetMapping<\/strong> que es requerido para generar nuestra consulta personalizada, agregamos la libreria primero<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">use Doctrine\\ORM\\Query\\ResultSetMapping;<\/pre>\n<p>Y ahora el codigo referente a esto<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\n\/\/Esto es requerido y debe ser coherente con nuestra consulta SQL\n\/\/ O no veremos los campos en el resultado\n$rsm = new ResultSetMapping;\n$rsm-&amp;gt;addEntityResult(&#039;ANPruebaBundle:Usuario&#039;, &#039;u&#039;);\n$rsm-&amp;gt;addScalarResult(&#039;id&#039;, &#039;id&#039;);\n$rsm-&amp;gt;addScalarResult(&#039;nombre&#039;, &#039;nombre&#039;);\n<\/pre>\n<p>En la documentaci\u00f3n nos indica que podemos ocupar <strong>addFieldResult<\/strong> en vez de <strong>addScalarResult<\/strong>, personalmente esto me generaba internamente otro array es decir el resultado que entregaba nuestro paginador era<\/p>\n<p>Con <strong>addFieldResult<\/strong>:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\narray (\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 array (\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 array (\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#x5B;&amp;quot;id&amp;quot;] =&amp;gt; 123\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#x5B;&amp;quot;nombre&amp;quot;] =&amp;gt; Ariel\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\u00a0\u00a0\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\u00a0\u00a0\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\u00a0\n<\/pre>\n<p>Con <strong>addScalarResult<\/strong>:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\narray (\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 array (\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#x5B;&amp;quot;id&amp;quot;] =&amp;gt; 123\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &#x5B;&amp;quot;nombre&amp;quot;] =&amp;gt; Ariel\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\u00a0\u00a0 <\/pre>\n<p>Ademas esta ultima nos permite ocupar campos alias, pero de esto hablaremos en otro POST o nos extenderemos mucho mas.<\/p>\n<p>Volviendo a nuestra consulta original, ya tenemos la consulta en SQL y agregamos ResultSetMapping, pues bien, ahora podemos generarla<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\n\/\/Doctrine\n$em = $this-&amp;gt;getDoctrine()-&amp;gt;getManager();\n\n$query = $em-&amp;gt;createNativeQuery($sql, $rsm);<\/pre>\n<p>Por ultimo, lo agregamos a nuestro paginador<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">$paginator\u00a0 = $this-&amp;gt;get(&#039;knp_paginator&#039;);\n\n$pagination = $paginator-&amp;gt;paginate(\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$query,\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0$this-&amp;gt;get(&#039;request&#039;)-&amp;gt;query-&amp;gt;get(&#039;page&#039;, 1)\/*page number*\/,\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0 10 \/*limit per page*\/\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0);<\/pre>\n<p>Y listo, ahora podriamos probarla, si tienes el Bundle\u00a0<a title=\"LadyBug\" href=\"https:\/\/github.com\/raulfraile\/LadybugBundle\" target=\"_blank\">LadyBug<\/a> para depurar puedes ocupar esto<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">ladybug_dump_die($pagination-&amp;gt;getItems());<\/pre>\n<p>Si no, pues de la forma tradicional<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">var_dump($pagination-&amp;gt;getItems());\nexit();<\/pre>\n<p>En resumen, lo mas complicado es generar la consulta con createNativeQuery, ya que si no se genera bien con su respectivo ResultSetMapping los campos no apareceran, antes de pasarsela a nuestro paginador te recomiendo probar los resultados que entrega<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">ladybug_dump(count($query-&amp;gt;getArrayResult()));<\/pre>\n<p>Fijate que ocupe <strong>getArrayResult<\/strong>, si ocupas otro como getResult <strong>no<\/strong> te entregara ningun resultado.<\/p>\n<p>Saludos<\/p>\n","protected":false},"excerpt":{"rendered":"<p>El Bundle\u00a0KnpPaginator es una excelente ayuda que nos permite paginar de una forma bastante amigable y facil, normalmente su uso se refiera a pasarle una query que formamos mediante createQuery y este la procesa y nos entrega el resultado paginado segun los parametros que definamos pero&#8230; \u00bfQue sucede cuando deseamos pasarle una consulta personalizada? la [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,7,12],"tags":[25],"class_list":["post-196","post","type-post","status-publish","format-standard","hentry","category-doctrine","category-knppaginator","category-symfony2-2","tag-symfony2"],"_links":{"self":[{"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/posts\/196","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/comments?post=196"}],"version-history":[{"count":0,"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/posts\/196\/revisions"}],"wp:attachment":[{"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/media?parent=196"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/categories?post=196"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/arielnavarrete.cl\/blog\/wp-json\/wp\/v2\/tags?post=196"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}