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 :)
Related
So I have a form containing questions of type free fields (text), multiple choice (checkbox), single choice (radio).
I get the values of the button checked and I save in database for each user but what I want to do is change this value like this:
data-point "2" = 10 points,
data-point "3" = 7 points,
data-point "4" = 4 points,
data-point "5" = 2 points,
and then with these values I have to do a calculation and define a profile according to the result .. how should I do that? SQL request? in the loop? a little help would be welcome ..here my twig to loop on subquestion.
<div class=" col-6 d-flex">
<div class="label-div1">
<label for="subscale{{subQuestion.id}}for{{i}}>
<img src="{{asset('images/survey/humeur/'~ i ~'.jpg')}}">
</label>
<input type="radio" class="radio-pict" name="{{subQuestion.id}}" id="subscale{{i}}" data-point="{{i}}"value="{{i}}">
</div>
here my controller to save de answers
public function saveAnswer(Request $request)
{
/* Repository */
$questionRepo = $this->getDoctrine()->getRepository(Question::class);
$answerRepo = $this->getDoctrine()->getRepository(Answer::class);
$choiceRepo = $this->getDoctrine()->getRepository(Choice::class);
$userSlpRepo = $this->getDoctrine()->getRepository(UserSlp::class);
/* Entity Manager */
$em = $this->getDoctrine()->getManager();
$datas = $request->request->all();
$userSlp = $userSlpRepo->findOneByGaeaUserId($this->getUser()->getId());
foreach ($datas as $data => $value) {
$questionId = explode("_", $data);
$question = $questionRepo->findOneById($questionId[0]);
switch ($question) {
case 'Sub_question_free':
$answer = new Answer_free;
$answer->setFreeAswere($value);
break;
case 'Sub_question_scale':
$answer = new Answer_scale;
$answer->setScale($value);
break;
$answer->setQuestion($question);
$answer->setUserSlp($userSlp);
$em->persist($answer);
$em->flush();
}
exit;
}
}
and here my twig to display on admin the results..
{% if answer.question == "Sub_question_choice" or
answer.question == "Sub_question_scale" or
answer.question == "Sub_question_free" %}
{% if answer == "choice" %}
{% for choice in answer.choices %}
<p>{{choice.name}}</p>
{% endfor %}
{% elseif answer == "free" %}
<p>{{answer.freeAswere}}</p>
{% elseif answer == "scale" %}
<p> {{answer.scale}}</p>
{% endif %}
So first, in Symfony a controller always need to send back a response. Even if it's just a redirect or a JsonResponse. So you shouldn't use exit in your controller.
You should use Symfony form, but if you are new to Symfony, I can understand that it's not easy.
Here to help you, you have many options:
You can change value with some conditions inside your swith statement in your controller
You can separate that in a service, look for service in Symfony on the docs. And use dependency injection inside your controller like this :
Service :
public function conversion(){
//Some conditions
}
controller:
public function saveAnswer(Request $request, YourService $service)
And use it inside as a function.
(More Easy Option) You can overload your setter in your entity (Answer.php for example):
public function yourSetter(...){
//do some conditions and after set your variables depending on result
}
Hope this will help you, feel free to ask if you want !
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
I'd really love if anyone can help, I'm trying to figure out a way to loop through two queries in Twig. I can create it in PHP but I'm having doing the same thing on Twig. This is how I'd normally do it on PHP:
foreach($items as $item){
$product_id = $item;
$products = $app->db->table('products')->where('id', $product_id)->first();
echo "<li>" . $products->title . "</li>";
}
The above code will work fine but on Twig it will not loop to the next loop, but it will keep on looping the same thing.
Kindly help if you know how I can use Twig for loop like I use it above. I'm querying it using Laravel Eloquent in Slim.
This is what I did:
The controller
$products = $app->db->table('products')->where('trash', '0')->first();
The View
{% for item in items %}
{% set product_id = item.id %}
<li> {{ products.title }}</li>
{% endfor %}
It will only show the first row and repeat the samething.
Don't attempt to run queries from a template. Do it in the controller and pass the result to the template.
Also $items appears to be an array of IDs, so you should be able to load all of the products at once with a where-in condition (instead of multiple queries):
Controller:
$products = $app->db->table('products')->whereIn('id', $items)->get();
// pass $products to the template as "products"
Twig template:
{% for product in products %}
<li>{{ product.title }}</li>
{% endfor %}
i'm trying to make a loop in php while using twig and inside this loop,
i am creating a parameter which contains the record from the database query.
The problem is that when I use the parameter in my HTML file, it only return 1 record from the while loop, even if there are 3 or 4 or even more..
This is the php code I have:
public function getWidgetsByName($name)
{
global $params;
$get = $this->db->query("SELECT * FROM profile_items
WHERE category = 'widget'
AND username = '". $name ."'");
if($get)
{
while($key = $get->fetch())
{
$params["profile_widget_name"] = $key['name'];
}
}
}
And this is the HTML parameter in my HTML twig rendered file:
{{ profile_widget_name }}
The paremeters just get rendered how they are supposed to be rendered:
echo $twig->render('app/views/'. $_REQUEST['p'] .'.html', $params);
And yes the $params variable is an array, in the config it file it first gets used as $params = array("..." => "..."); and I add things to this array by doing $params["..."] = "...";
So, I hope someone can help me.
Thanks in advance,
Best Regards.
At the moment, the value of $params["profile_widget_name"] is just one string. Every time you go through the while loop, you overwrite the previous value of the key with the current value.
So when you pass $params to Twig, the value of profile_widget_name is the value of name in the last row of the database to be selected.
I think what you want instead is for the value of profile_widget_name to be an array. Then every time you go through the loop, the current value of name is added to the array, instead of overwriting it.
You do this by doing something like:
$params["profile_widget_names"][] = $key['name'];
Now in your Twig template, you'll need to do something like:
{% for profile_widget_name in profile_widget_names %}
{{ profile_widget_name }}
{% endfor %}
Using Multiple Parameters
If you want multiple parameters to be on there, you can do this:
$params["profile_widgets"][] = [
'pos_x' => $key['pos_x'],
'name' => $key['name'],
];
And in Twig:
{% for profile_widget in profile_widgets %}
Name: {{ profile_widget.name }}
Pos X: {{ profile_widget.pos_x }}
{% endfor %}
I'm trying to display data from database.
This is my routing:
pages:
pattern: /pages/{id}
defaults:
_controller: DprocMainBundle:Index:show
This is the method for that route:
public function showAction($id)
{
$page = $this->getDoctrine()
->getRepository('DprocMainBundle:Pages')
->find($id);
if (!$page) {
throw $this->createNotFoundException('No product found for id '.$id);
}
return $this->render('DprocMainBundle:Dproc:single.html.twig',array('pages' => $page));
}
print_r($page) displays:
Dproc\MainBundle\Entity\Pages Object
(
[Id:protected] => 1
[page_title:protected] => A Foo Bar
[page_content:protected] => Test content for page
[page_category:protected] => 3dsmax
)
In single.html.twig im trying to display that information:
{% for page in pages %}
{{ page.page_title }}
{% endfor %}
It shows nothing, what i'm doing wrong?
The method find() will just return a single result, not an array. If you did findBy(array('id'=>$id)) then you would get an array back with one result. Alternatively you could just not loop through in your template because there's no need when you have only one result. Just change your variable name to page and use {{ page.page_title }}
You used two differents entity.. one is in getRepository('DprocMainBundle:Pages') and other is in render('DprocMainBundle:Dproc:single.html.twig',array('pages' => $page)). You should put there the same entity i.e DprocMainBundle:Pages . I hope this you success.
Try it without the for loop. Because you have post data from the database, you can try this:
{{ pages.page_title }}