In Symfony2, how will i highlight the word i search from the search box :
// Search Twig:
{% block body -%}
<h1>Results of "{{ find }}"</h1>
{% if entities%}
{% for entity in entities %}
<table class="record_properties">
<tbody>
<tr>
<td>>{{ entity.question }}</td>
</tr>
</tbody>
</table>
{% endfor %}
{%else%}
<td>No Results found</td>
{%endif%}
{% endblock %}
//searchController :
public function searchAction() {
$request = $this->getRequest();
$data = $request->request->all();
$find = $data['search'];
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT p.id, p.question
FROM EpitaEpitaBundle:questionanswer p
WHERE p.question LIKE :data')
->setParameter('data', "%$find%");
$res = $query->getResult();
return $this->render('EpitaEpitaBundle:questionanswer:search.html.twig', array(
'entities' => $res,
'find' => $find));
}
// I am getting the searched result but i want it to be highlighted...
You could, probably, do something like:
<td>>{{ entity.question|replace({find: "<span class='highlight'>" ~ find ~ "</span>"}) }}</td>
And then create a class in css:
span.hightlight {
/* Whatever you want to do with highlighted elements*/
}
Another option would be to write your own filter in PHP.
I'll suggest you to create your own filter as suggest by #Mindastic.
For that you will need a custom Twig extension, follow this cookbook entry to do that.
You will need to mark the output as safe HTML to make it work.
Last, but not least, for security reason, I encourage you to use PHP strip_tags() function on the input value to avoid injection.
Related
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?
I am new to symfony/doctrine and when setting up my first table and query I am struggling with the best way to output results in Twig.
So, I have this method on \AppBundle\Controller\BrandsController
public function showAction($brand)
{
$product = $this->getDoctrine()
->getRepository('AppBundle:Brands')
->findOneByBrand($brand);
if (!$product) {
throw $this->createNotFoundException(
'No product found for id '.$brand
);
}
return $this->render('brands/brands.html.twig', [
'product' => $product
]);
}
This produces an object like below, which I cannot iterate over.
Brands {#459 ▼
-brand_id: 24
-brand: "Ford"
-active: "Y"
-img_logo: "/img/brand/ford.png"
-img_logo_small: "/img/brand/ford_20.png"
-img_logo_big: "/img/brand/ford-big.png"
}
Of course I can create a query like below, but that negates the benefit of the findBy() method:
$repository = $this->getDoctrine()
->getRepository('AppBundle:Brands');
$query = $repository->createQueryBuilder('p')
->where('p.brand = :brand')
->setParameter('brand', $brand)
->getQuery();
$product = $query->getSingleResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
I found similar questions, like this one, but they mess up with the array keys by giving a array that looks like:
array:6 [▼
"\x00AppBundle\Entity\Brands\x00brand_id" => 24
"\x00AppBundle\Entity\Brands\x00brand" => "Ford"
"\x00AppBundle\Entity\Brands\x00active" => "Y"
"\x00AppBundle\Entity\Brands\x00img_logo" => "/img/brand/ford.png"
"\x00AppBundle\Entity\Brands\x00img_logo_small" => "/img/brand/ford_20.png"
"\x00AppBundle\Entity\Brands\x00img_logo_big" => "/img/brand/ford-big.png"
]
By the way, that's the simple version of the code on brands/brands.html.twig:
{% for item in product %}
<p> This is my {{ item }}</p>
{% endfor %}
Is there a clean way to do it?
Thanks
The idea is to use Entity Serializer Doctrine Script , look to the link below :
https://github.com/borisguery/bgylibrary/blob/master/library/Bgy/Doctrine/EntitySerializer.php
Just copy the script into a new class : EntitySerializer.php , under AppBundle\Controller , then in your BrandsController class:
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AppBundle:Brands') ->findOneByBrand($brand);
$serializer = new EntitySerializer($em);
$product = $serializer->toArray($product);
and finnally you can easily iterate the array :
foreach ($array as $key => $value) {
echo 'the key is '.$key.'<br>';
echo 'the value is '.$value;
}
General...
It's kinda bad idea to iterate through your product in twig like an array. Just because it's matter of time till you'll need more controll like show/do this if product is active or not... and so on. So pretty soon you'll have to do somthing like this (just an wild example of possible usecase...)
{% if product|default %}
{# if logo isn't there or empty or similar use path to no-logo.png as fallback #}
{% set _product_logo_path = product.img_logo|default('/assets/images/no-logo.png') %}
{{ product.brand }} <img src="{{- _product_logo_path -}}" title="blah blah" />
{% if not product.isActive|default %}
<span>currently unavailable</span>
{% endif %}
{% endfi %}
Architectual...
You can use your repository for that kind of "manipulation"
something like :
in your controller:
$product = $this->getDoctrine()
->getRepository('AppBundle:Brands')
->findOneForViewByBrand($brand);
// concider to use instanceof since you expecting Brands object
// if not stay with !$product
if( false === $product instanceof Brands )
{
// not found exception..
}
in your Repo-Class
public function findOneForViewByBrand( $brand )
{
// you ca make use of PARTIAL SELECTS
// NOTE: if you use PARTIAL selects you have to select at least id, like PARTIAL p.{brand_id}
$query = $this->createQueryBuilder('p')
->select('PARTIAL p.{brand_id, brand, active, img_logo, img_logo_small, img_logo_big}')
->where('p.brand = :brand')
->setParameter('brand', $brand)
->getQuery();
// If found you'll get a Brand BUT ONLY field in curly brackets will be populated with data, others will be just NULL
return $query->getOneOrNullResult(\Doctrine\ORM\AbstractQuery::HYDRATE_ARRAY);
}
and then in your twig
{% for fieldName, fieldValue in product %}
{# should skip null values #}
{% if fieldValue is defined and fieldValue is not empty % }
<p> This is my {{ fieldValue }}</p>
{% endif %}
{% endfor %}
About Partial Selects
P.S. code is untested and it's here so you can get an idea ;) I don't know what version of symfony you use and what version of doctrine is installed, so if one of mentioned methods doesn't exists - please don't be angry :)
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
I installed Userfrosting on the same hosting where I have Prestashop installed also. Userfrosting uses the same database as the Prestashop.
I want to create a page where the users registered on Userfrosting can review their sales on my ecommerce. (Prestashop supplier = userfrosting user)
How can I make a custom SQL query on that page? I found a query where I can filter sales by suppliers on prestashop, but don't know how to implement it with UserFrosting (it uses html pages).
Although I dont recommend this method, but this a simple way to query the table and show information on a page without getting into Userfrostings MVC.
In index.php just below feature pages comment use this code to register a page having a url as /mysales, it also fetches user information from the user_sales table and renders mysales.twig to show the information.
$app->get('/mysales/?', function () use ($app) {
// Access-controlled page
if (!$app->user->checkAccess('uri_dashboard')){
$app->notFound();
}
$db_config = $app->config('db');
$db_mysqli = new mysqli($db_config['db_host'], $db_config['db_user'], $db_config['db_pass'], $db_config['db_name']);
$sales_rows = array();
$result = $db_mysqli->query('select `description`, `total` from `user_sales` where `user_id` = 10 ');
if($result){
while($row = $result->fetch_assoc()) {
$sales_rows[] = $row;
}
}
$app->render('mysales.twig', [ //calls to render mysales.twig
'sales_rows' => $sales_rows //passing variables to twig
]);
});
Now create mysales.twig inside userfrosting/templates/themes/default folder having the code to display content from sales_rows twig variable. This will extend the dashboard-layout.twig so that the navigation panels stay in place.
{% extends "layouts/layout-dashboard.twig" %}
{% set page_group = "dashboard" %}
{% block page %}
{% set page = page | merge({
"title" : "My Sales",
"description" : ""
}) %}
{{ parent() }}
{% endblock %}
{% block content %}
<h1>My Sales</h1>
<table border="1">
<tbody>
<tr>
<td><strong>Description </strong></td>
<td><strong>Total </strong></td>
</tr>
{% for sales in sales_rows %}
<tr><td>{{ sales.description }}</td><td>{{ sales.total }}</td></tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
thats it now if you log in and navigate to /mysales you should get the information from the table.
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 }}