Limit, pagination in Symfony - php

I am trying to add pagination to my current project. I am pretty new to Symfony so I am not sure if there is something out there that can help me build such. My current code looks like this:
Controller class:
class MovieDisplayController extends Controller
{
public function showAction()
{
$movies = $this->getDoctrine()->getEntityManager()->getRepository('AppBundle:Movie')->FindAll();
return $this->render('movies/index.html.twig', array(
'movies' => $movies
));
}
}
Twig template:
{% block body %}
{% if movies|length == 0 %}
There are no movie items available. Add a movie here to get started.
{% elseif movies|length != 0 %}
These are the results: <br />
<ul>
{% for x in movies %}
<li>Title: {{ x.title }} - Price: {{ x.price }} - Edit - Details - Delete</li>
{% endfor %}
</ul>
Add more movie entries
{% endif %}
{% endblock %}
This will return all results within the database. I would like to only show 5 results (rows per page) and add paging buttons below the list and I wonder how/if this is possible?

The findAll() function will not work if you want to set limit.
You can try KnpPaginatorBundle to add pagination in symfony. It will work with fine to add pagination.
https://github.com/KnpLabs/KnpPaginatorBundle

Related

Custom dynamic Twig template that extends Twig_Template

TL;DR: I am currently struggling with the idea how to elegantly generate CRUDs/GRID using Twig.
Long story:
I have a Phalcon app with AngularJs, templating system is Twig.
PHP is suppose to prepare a template for CRUD that contains:
List od entities, table that contains proper headers, columns, row items, action buttons
Simple and advanced search
Mass actions block
Main buttons - Add/Import/Export etc.
AngularJs is responsible for:
Paginating results in 2 modes: Ajax Loaded entities, All entities at once
Changing number of entities per page
Displaying various forms in modals - Add/Edit/Import
Filtering entities - simple and advanced search
Everything is working right now but I am not particularly fond of how it works.
For example a simple entity with few fields needs a following template:
{% extends 'index.twig' %}
{% block containerAttr %}ng-controller="InventoryController as ctrl"{% endblock %}
{% set ctrlName = 'ctrl' %}
{% set columnWidth = 'col-xs-12' %}
{% set tableClass = 'table table-bordered table-striped table-hover' %}
{% block header %}
{% include '#crud/crud/header.twig' %}
{% endblock %}
{% block content %}
{% embed '#crud/crud/main-box.twig' %}
{% block tableHeader %}
{% autoescape false %}
{{ crud.tableHeader('Id', 'id') }}
{{ crud.tableHeader('Nazwa', 'name') }}
<th class="col-xs-1">Actions</th>
{% endautoescape %}
{% endblock %}
{% block tableRow %}
<td ng-bind="e.id"></td>
<td ng-bind="e.name"></td>
{% endblock %}
{% endembed %}
{% embed '#crud/crud/form.twig' %}
{% block modalBody %}
{% autoescape false %}
<div class="row">
{{ crud.input('ctrl.entity.name', 'Name', 12) }}
</div>
{% endautoescape %}
{% endblock %}
{% endembed %}
{% embed '#crud/crud/upload.twig' %}{% endembed %}
{% embed '#crud/crud/advancedSearch.twig' %}{% endembed %}
<script>
window.xdata = {{ xdata | json_encode | raw }};
</script>
{% endblock %}
crud in this template is responsible for generating form fields compatible with AngularJs.
Idea is: To create new CRUDs/Grids with the least amount of work required. While still giving a room for some flexibility.
Right now to make a CRUD/Grid inside my application there is a really little work required:
PHP Controller needs to contain only one line:
class SubCategoryController extends CrudBase
{
protected $entityClass = InventoryCategory::class;
}
Thanks to extending it is really easy to customize every part of it.
Model needs to have just a few fields:
class InventoryCategory extends ModelBase
{
/** #var integer */
public $id;
/** #var string */
public $name;
public function initialize()
{
$this->setSource('inventory_subcategory');
$this::setup(['castOnHydrate' => true]);
}
}
Even the AngularJs controller contains only URLs and one line:
class InventoryCategoryController extends CrudBase {
constructor($injector, $scope) {
super($injector, $scope);
this.urlSave = '/inventory/category/save';
this.urlImport = '/inventory/category/import';
this.urlExport = '/inventory/category/export';
this.urlDelete = '/inventory/category/delete';
this.init({
advancedSearch: false
});
}
}
angular.module('App').controller('InventoryCategoryController', InventoryCategoryController);
Yes I use ES6, application is not compatible with older browsers anyways and it is admin panel so I do not care about older browsers.
But now only templates remains a tedious work to copy it over and change it. Not to mention they do look pretty ugly.
So my idea was to use Annotation Reader (Phalcon has one) to read fields from Model, then transform it into a base configuration, check if there is custom configuration in json and merge it. Then based on configuration, generate this whole template on the fly.
So the grand question is:
I was wondering if I can create some class that extends Twig_Template to generate this template on the fly?
I have seen Twig cache and it should be pretty easy but then how can i use it? How can i render it? Will extend still work?

Phalcon & Volt: Adding hasMany()/belongsTo() conditions, and counting linked rows

I have two tables in my database, form_settings and webmaster, that are on a one-to-many relationship, and this has been defined in their Models.
FormSettings.php
class FormSettings extends \Phalcon\Mvc\Model
{
public function initialize()
{
$this->hasMany('db_table', 'webmaster', 'db_table');
}
}
Webmaster.php
class FormSettings extends \Phalcon\Mvc\Model
{
public function initialize()
{
$this->belongsTo('db_table', 'form_settings', 'db_table');
}
}
In my Controller, I perform the following find() and pass it to the view:
ControllerBase.php
class ControllerBase extends Controller
{
public function initialize()
{
$exhibitions = FormSettings::find(
array(
'form_type = "v" AND show_end_date > NOW() AND archived = "n"',
'order' => 'db_table'
)
);
$this->view->exhibitions = $exhibitions;
}
}
And I know it's correctly linking rows from my webmaster table, as I have the following code in my View, and it displays webmaster_id values:
index.volt
{% for exhibition in exhibitions %}
<li>
{{ link_to('index/browse/' ~ exhibition.db_table, exhibition.db_table) }}
<!-- testing below -->
{% for webm in exhibition.webmaster %}
{{ webm.webmaster_id }}
{% endfor %}
<!-- end testing -->
</li>
{% endfor %}
My question is three-part:
How can I only link webmaster rows that have a column extra_1 as not NULL?
How can I count() the linked webmaster rows for each db_table (which is unique in form_settings)?
How can I pass this information through to the View in my $exhibitions object so that I can echo the count() in Volt syntax?
Hey and first of all thank you for the nice question formatting.
Excuse me for using examples that use my current database structure. But you can easily update your code.
1) You can set additional parameters to the relation definition.
$this->hasMany('id', 'Models\News', 'category_id', [
'alias' => 'news',
'reusable' => true,
'params' => [
'order' => 'id DESC',
'conditions' => 'extra_1 IS NOT NULL',
]
]);
Please note the reusable above. When using it, the query runs only once per request. Considering you want to count records and iterate over them its a nice performance boost.
2 + 3) Iterating over results in volt and counting:
Controller code:
$this->view->categories = \Models\NewsCategories::find();
Volt:
{% for category in categories %}
{% if category.news|length > 0 %} // Do not print categories without articles
<h3>Category #{{ category.id }} with total of {{ category.news|length }} articles.</h3>
<ul>
{% for item in category.news %}
<li>News #{{ item.id }}</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
The above code in my case produces the following output:
Category #4 with total of 4 articles.
News #3
News #4
News #5
News #7 Category #5 with total of 1 articles.
News #1

Loop through categories, childcategories and products in twig

My table structure in my database is like this:
Some categories have a child category and some not. A product can belong to:
Child category
Parent Category (this category has NO child categories)
My array looks like this:
Category A is a parent category. Category B - Head is also a parent category. Category B - Child is a child category of B - Head.
Now I would like to show this array like this:
But I'm stuck on how to know if it's a category or a list of products. Can someone help me with this?
If you're using Doctrine Models (which if you're using Symfony, you should be) then all you're doing is looping through the methods on the object.
Quick and dirty example with few assumptions e.g. using #Template() annotation and standard DAOs aka EntityManager[s] as well as having getChildren() and getProducts() methods on the Category.php (AKA model/entity)
On the controller
/**
* #Route("/products", name="all_products")
* #Template()
*/
public function someAction()
{
...
$categories = $this->getCategoryManager()->findBy([]);
...
return [
'categories' => $categories
];
}
In your twig template
{% if categories|length > 0 %}
{% for category in categories %}
{% if category.children|length > 0 %}
... Here you create the HTML for nested ...
{% else %}
... Here you create the HTML for Category ...
{% for product in category.products %}
... Here you create the HTML for products ...
{% endfor %}
{% endif %}
{% endfor %}
{% else %}
.... some html to handle empty categories ....
{% endif %}
If the HTML for nested is repeated in the HTML for flat (very likely scenario) then you can create and include a macro to spit that out for you.
This is basic, but I think it pretty much covers what you're asking if I'm understanding your question properly.
Btw, you should definitely read the docs for twig and Symfony since they have examples like these everywhere.
I'll edit this answer if you respond as appropriate. Right now you haven't posted enough information to really guide you properly but hope this helps.
You could use a recursive macro. In the macro you either print the list of products or print the list of categories and then call itself.. and so on...
{% macro navigation(categories, products) %}
{% from '_macros.html.twig' import navigation %}
{% if categories|length > 0 or products|length > 0 %}
<ul>
{% for category in categories %}
<li>
{{ category.name }}
({{ category.children|length }} child(ren) & {{ category.products|length }} products)
{{ navigation(category.children, category.products) }}
</li>
{% endfor %}
{% for product in products %}
<li>{{ product.name }}</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
You would just use this in a template like...
{% from '_macros.html.twig' import navigation %}
{{ navigation(array_of_categories) }}
This just creates a basic set of nested unordered lists but then can be used with any HTML you want, obviously.
For a fiddle see http://twigfiddle.com/mzsq8z).
The fiddle renders as the following (twigfiddle only shows the HTLM rather than something you can use to visualize)...

Avoid repeating the same query in controllers for parent template

I am trying to make a messaging system and I'm facing a small problem. I have a bigger template that displays my menu and my content. The menu includes the number of new messages, and the content can be any page(compose a new message, inbox, sent).
The problem is that I have to render each of the small templates by passing the number of new received messages to each of them, calling the doctrine each time and repeating code. Is there any way to send the number only to the parent template?
Here are my templates:
This is the parent containing the newmsg variable that gives me problems.
{% extends "::base.html.twig" %}
{% block body %}
inbox : {{ newmsg }}
sent
compose
{% endblock body %}
Here is an example of child template:
{% block body %}
{{ parent() }}
{% if messageList %}
{% for message in messageList %}
<li>title = {{ message.title|e }}</li>
<li>cont= {{ message.content|e }}</li>
<li>data= {{ message.date|date('d-m-Y H:m:s') }}</li>
<li>sender= {{ message.sender|e }}</li>
<hr>
{% endfor %}
{% else %}
<div>no messages</div>
{% endif %}
{% endblock body %}
The problem is that each child template is asking me for the newmsg variable
$messages = $this->getDoctrine()->getRepository('MedAppCrudBundle:Message');
$newMessagesNo = count($messages->findBy(array('seen' => '0', 'receiver' => $this->getUser())));
return $this->render(
'MedAppCrudBundle:UserBackend\Message:new.html.twig',
array(
'form' => $form->createView(),
'newmsg' => $newMessagesNo,
)
);
And I have to write this in every single controller. Any way I can shorten this problem?
You could implement a service that gives back the newmsg value and call it on your parent template. Then it would not be necessary to pass the variable.
You can add a service in your bundle's services.yml with something like:
services:
newmessages:
class: Full\Class\Name\NewMessagesService
arguments: ["#doctrine.orm.entity_manager"]
Then, implement the Full\Class\Name\NewMessagesService class. Keep in mind that this class will need a constructor that receives an EntityManager argument. Something like:
<?php
namespace Full\Class\Name;
class NewMessagesService{
private $entityManager;
public function __construct($entityManager){
$this->entityManager = $entityManager;
}
public function methodToCalculate(){
//Perform calculation and return result
}
}
Then, in your parent template, replace {{newmsg} with:
{{ newmessages.methodToCalculate() }}

Symfony2 Twig Get Total Count for Child Entity

The following entities exist, Farm, Barn and Animals. A Farm can have many Barns and a Barn many Animals.
When displaying a Farm in a TWIG template the number of Animals should be shown as well.
What is the best way to do this?
I have create a TWIG extension which allows me to easily show the number of barns.
public function totalFieldFilter($data, $getField='getTotal') {
$total = count($data->$getField());
return $total;
}
In my template I would use {{ farm|totalField('getBarns') }}, I could easily extend this to write another custom function like so:
public function totalFieldFilter($farm) {
$total = 0;
foreach($farm->getBarns() AS $barn) {
$total += count($barn->getAniamls());
}
return $total;
}
Although this would work, is there a better way and can it be made more generic? What if I wanted to count Legs on Animals? Or how many Doors a Barn has, I would have to write a custom TWIG extension each time.
Use Entity accessors :
{% for farm in farms %}
{{ farm.name }}
{% set barns = farm.getBarns() %}
Barns count = {{ barns|length }}
{% for barn in barns %}
{% set animals = barn.getAnimals() %}
{{ barn.name }} animals count : {{ animals|length }}
{% endfor %}
{% endfor %}
You are looking for the length filter
When used with an array, length will give you the number of items. So, if your array is farm.barns, you can just use {{ farm.barns|length }}

Categories