Can't access every object property in Twig - symfony

I have a News entity with the next properties:
text
start_date
expire_date
Here's part of the class:
//News.php
/**
* #ORM\Column(type="string", length=200)
*/
private $text;
/**
* #ORM\Column(type="datetime")
*/
private $start_date;
/**
* #ORM\Column(type="datetime")
*/
private $expire_date;
I want to show all the news in a list in a twig template, this is how I'm doing that:
<div class="list-group">
{%for news in news%}
<div class="panel panel-default">
<div class="panel-heading">{{ news.start_date|date('Y-m-d') }}</div>
<div class="panel-body">
{{ news.text }}
</div>
</div>
{%endfor%}
</div>
The fact is that I get the following error:
Neither the property "start_date" nor one of the methods "start_date()", "getstart_date()"/"isstart_date()" or "__call()" exist and have public access in class "AppBundle\Entity\News".
But If I try to render only the text property, I am able to access it.
Why is that?

Your attribute is private. you can add a getter
public fucntion getStartDate() {
return $this->start_date;
}
and in twig
{{ news.startDate|date('Y-m-d') }}

Related

Did we migrate incorrectly? Validation not happening in forms migrated from Symfony 2.7 to 4.0

In code migrated from Symfony 2.7 to 4.0, validation no longer happens on my form, allowing bad data to pass through and cause a Doctrine constraint violation
I'm new to Symfony and was asked to migrate a 2.7 application to 4.0. I did this in steps (2.7->2.8->3.x->4.0) and addressed issues as they came up, but one thing that broke along the way is automatic form validation. In the original version, if I attempted to create a new user and left the fields blank, it would correctly flag those and pop up " must not be empty" messages in the UI. Now, it lets those past until it attempts to write to the database, at which point Doctrine barfs because the database not null constraints are violated.
I've tried to figure out what I'm doing wrong, but I don't have a firm grasp on how the form creation process and syntax has changed. All of the example documentation on validation in forms assumes the createFormBuilder() approach, and all my existing code uses createForm(). What am I missing?
Here's part of the user object associated with the form showing the #Assert statements that I expect to trigger validation warnings:
/**
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="Domain\CoreBundle\Repository\UserRepository")
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity(fields="email", message="This email address is already in usage")
* #UniqueEntity(fields="username", message="This username is already in usage")
*/
class User extends BaseUser implements JsonSerializable
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Assert\NotBlank(message="Email should not be empty")
* #Assert\Email(strict=true)
* #Assert\Length(max=150, maxMessage="Email should be less than {{ limit }} characters")
*/
protected $email;
/**
* #Assert\NotBlank(message="Username should not be empty")
* #Assert\Regex(
* pattern = "/^\d*[a-zA-Z][ a-zA-Z0-9\!\#\#\$\%\^\&\-\_\=\+\~\?\.]*$/i",
* message = "Username should include at least one letter"
* )
*/
protected $username;
/**
* #var string
*
* #Assert\NotBlank(message="First name should not be empty")
* #ORM\Column(name="first_name", type="string", length=255)
*/
protected $firstName;
/**
* #var string
*
* #Assert\NotBlank(message="Last name should not be empty")
* #ORM\Column(name="last_name", type="string", length=255)
*/
protected $lastName;
(rest of code omitted for conciseness)
And here's the addNew action from the controller (AdministratorController extends UserController):
/**
* Add new administrator
*
* #param Request $request
*
* #return Response
*/
public function addNewAction(Request $request)
{
$company = $this->getCurrentCompany();
$form = $this->createForm(AddAdministratorType::class, null,
array('current_user'=> $this->user, 'restricted_admin'=>$this->getRestrictedAdmin(), 'company'=>$company));
if ($request->getMethod() == Request::METHOD_POST) {
$form->handleRequest($request);
// check if the user already exists
$userManager = $this->get('fos_user.user_manager');
$user = $form->getData();
$oldUser = $userManager->findUserByUsername($user['email']);
if ($oldUser)
{
$alreadyExists = false;
if ($user["isSuperAdmin"] &&$oldUser->isGrantedSuperAdmin())
$alreadyExists = true;
if ($user["isCompanyAdmin"] && $oldUser->isGranted(UserRepository::ROLE_COMPANY_ADMIN, $company))
$alreadyExists = true;
if (!$user["isCompanyAdmin"] && !$user["isSuperAdmin"] && $oldUser->isGranted(UserRepository::ROLE_ADMIN,$company))
$alreadyExists = true;
if ($alreadyExists)
$form->get('email')->addError(new FormError('This email address is already in use'));
}
if ($form->isValid()) {
$user = $form->getData();
if ($oldUser) // if the user already exists, we just need to add the role
{
if (!$this->getUser()->isGrantedSuperAdmin() &&
!in_array($company->getId(), array_map(function($x){return $x->getId();}, $oldUser->getCompaniesWithRole())))
{
// the user isn't currently in this company and the user adding the role
// isn't a super admin, so we have to create a shadow user entry to hide
// the real user info from other in the company until the user logs into
// the company
$oldShadow=$this->em->getRepository(ShadowUser::class)->findOneBy(array("user" => $oldUser, "company"=>$company));
if (!$oldShadow)
{
$shadow = new ShadowUser();
$shadow->setUser($oldUser);
$shadow->setFirstName($user["firstName"]);
$shadow->setLastName($user["lastName"]);
$shadow->setCompany($company);
$shadow->setIsVydioUsed($user["isVydioUsed"]);
$shadow->setVydioRoomLink($user["vydioRoomLink"]);
$shadow->setCreatedDate(new \DateTime());
$this->em->persist($shadow);
}
}
if ($user["isSuperAdmin"])
{
$oldUser->addMyRole(UserRepository::ROLE_SUPER_ADMIN, $company);
$this->get('pp_mailer')->onAddNewRole($oldUser,UserRepository::ROLE_SUPER_ADMIN, $company );
}
if ($user["isCompanyAdmin"])
{
$oldUser->addMyRole(UserRepository::ROLE_COMPANY_ADMIN, $company);
$this->get('pp_mailer')->onAddNewRole($oldUser,UserRepository::ROLE_COMPANY_ADMIN, $company );
}
if (!$user["isSuperAdmin"] && !$user["isCompanyAdmin"])
{
$oldUser->addMyRole(UserRepository::ROLE_ADMIN, $company);
$this->get('pp_mailer')->onAddNewRole($oldUser,UserRepository::ROLE_ADMIN, $company );
}
$programRepo = $this->em->getRepository(ProgramUser::class);
foreach($user["programs"] as $program)
{
$oldRelation = $programRepo->findOneBy(array("user"=> $oldUser, "program"=>$program));
if (!$oldRelation)
{
$relation = new ProgramUser();
$relation->setUser($oldUser);
$relation->setProgram($program);
$relation->setCompany($company);
$this->em->merge($relation);
}
}
$this->em->persist($oldUser);
$this->em->flush();
}
else
{
$newUser = new User();
$newUser->setPassword($this->get('domain_core_service')->generatePassword());
$newUser->setDefaultCompany($company);
$newUser->setFirstName($user["firstName"]);
$newUser->setLastName($user["lastName"]);
$newUser->setEmail($user["email"]);
$newUser->setUsername($user["email"]);
$newUser->setEnabled($user["enabled"]);
$newUser = $this->em->getRepository('DomainCoreBundle:User')->addUserInSystem($userManager, $newUser);
$token = $this->get('domain_core_service')->generateToken();
$newUser->setConfirmationToken($token);
if ($user["isSuperAdmin"])
{
$newUser->addMyRole(UserRepository::ROLE_SUPER_ADMIN, $company);
$this->get('pp_mailer')->onAddNewUser($newUser,UserRepository::ROLE_SUPER_ADMIN, $company );
}
if ($user["isCompanyAdmin"])
{
$newUser->addMyRole(UserRepository::ROLE_COMPANY_ADMIN, $company);
$this->get('pp_mailer')->onAddNewUser($newUser,UserRepository::ROLE_COMPANY_ADMIN, $company );
}
if (!$user["isSuperAdmin"] && !$user["isCompanyAdmin"])
{
$newUser->addMyRole(UserRepository::ROLE_ADMIN, $company);
$this->get('pp_mailer')->onAddNewUser($newUser,UserRepository::ROLE_ADMIN, $company );
}
foreach($user["programs"] as $program)
{
$relation = new ProgramUser();
$relation->setUser($newUser);
$relation->setProgram($program);
$relation->setCompany($company);
$this->em->merge($relation);
}
$this->em->persist($newUser);
$this->em->flush();
}
return $this->redirect($this->generateUrl('domain_admin_show_all_administrators_page'));
}
}
return $this->render(
'DomainAdminBundle:Administrators:add-new.html.twig',
array(
'form' => $form->createView(),
'page_title' => 'Add New Administrator',
'currentSidebar' => $this->currentSideBar,
'currentSidebarItem' => $this->currentSidebarItem,
)
);
}
And the twig file for the form:
{% extends 'DomainAdminBundle::base-admin-layout.html.twig' %}
{% import '::widgets/form_errors.html.twig' as form_custom_errors %}
{% import '::widgets/label.html.twig' as form_custom_labels %}
{% block title %} My Application| {{ page_title }} {% endblock %}
{% block javascripts %}
{{ parent() }}
<script type="text/javascript" src="{{ asset('assets/scripts/admin-add-new.js') }}"></script>
{% endblock %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" type="text/css" href="{{ asset('assets/styles/admin-add-new.css') }}">
{% endblock %}
{% block admin_main_content %}
<div class="content-block administrator-controller" ng-controller="AdministratorController">
<div class="content-title-bar">
<div class="pull-left">
<h2>{{ page_title }}</h2>
</div>
</div>
<div class="content-block" ng-controller="AdminController">
{{ form_start(form, {"attr": { "action":"{{ path('domain_admin_add_new_administrator_page') }}", 'enctype': 'multipart/form-data', "method":"POST", "novalidate":"novalidate", "autocomplete":"off", "class":"form-horizontal add-user", "ng-submit":"disableAddButton()" }}) }}
<div class="base-box info-block">
<div class="control-group">
<div class="controls">
{{ form_widget(form.enabled) }}
{{ form_label(form.enabled, 'Active') }}
</div>
</div>
{% if app.user.isGrantedSuperAdmin() %}
<div class="control-group">
<div class="controls">
{% set companyAdminValue = form.isCompanyAdmin.vars.checked ? 'true' : 'false' %}
{{ form_widget(form.isCompanyAdmin, { 'attr':{ 'ng-model':'adminForm.isCompanyAdmin', 'ng-init': 'adminForm.isCompanyAdmin=' ~ companyAdminValue } }) }}
{{ form_label(form.isCompanyAdmin, 'Company Admin') }}
{% set superAdminValue = form.isSuperAdmin.vars.checked ? 'true' : 'false' %}
{{ form_widget(form.isSuperAdmin, { 'attr':{ 'ng-model':'adminForm.isSuperAdmin', 'ng-init': 'adminForm.isSuperAdmin=' ~ superAdminValue } }) }}
{{ form_label(form.isSuperAdmin, 'Super Admin') }}
</div>
</div>
{% endif %}
<div class="control-group" ng-init="initMultiSelect(true)">
{{ form_custom_labels.widget(form.programs) }}
<div class="controls">
{{ form_widget(form.programs) }}
{{ form_custom_errors.widget(form.programs) }}
</div>
</div>
<div class="control-group">
{{ form_custom_labels.widget(form.firstName) }}
<div class="controls">
{{ form_widget(form.firstName) }}
{{ form_custom_errors.widget(form.firstName) }}
</div>
</div>
<div class="control-group">
{{ form_custom_labels.widget(form.lastName) }}
<div class="controls">
{{ form_widget(form.lastName) }}
{{ form_custom_errors.widget(form.lastName) }}
</div>
</div>
<div class="control-group">
{{ form_custom_labels.widget(form.email) }}
<div class="controls">
{{ form_widget(form.email) }}
{{ form_custom_errors.widget(form.email) }}
</div>
</div>
<div class="control-group">
{{ form_custom_labels.widget(form.timezone) }}
<div class="controls">
{{ form_widget(form.timezone) }}
{{ form_custom_errors.widget(form.timezone) }}
</div>
</div>
</div>
<div class="text-right">
<button id="add-admin-submit" type="submit" class="btn btn-do" ng-disabled="isDisabled">Add new administrator</button>
Cancel
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
</div>
{% endblock %}
If I leave all fields blank and click "Add New Administrator" it doesn't flag them as blank, instead passing them onto Doctrine. The expected behavior is that it flags them at the UI and doesn't attempt to write them to the database.
I'm sure I've created multiple crimes against Symfony as I've ramped up the learning curve, so go easy. Right now I'm just trying to address this narrow issue; refactoring to more elegantly fit Symfony 4 will have to wait for another day.
Thanks!
Looks like you want to validate the User class against the data from your request.
Have you set data_class option in your form type class?
It's required if you want to use validation rules from another class (as you marked your properties with some #Assert* annotations).
https://symfony.com/doc/current/forms.html#creating-form-classes
Another way to do validation is to choose validation rules right in your FormType.

How to solve passing IDs in URL with Symfony

i'm making an app to make appointments with doctors, and in the template of the patient I need to use the id of the entity doctor in the path because I use it in the code of my controller, and I still can't get the Id of the entity doctor because when i do {{ doctor.id }} nothing appears on my screen, the content is empty.
As a solution I used the email of the doctor in the URL but it is quite ugly.
This is the code of the controller
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(MedecinRepository $repoMedecin, ObjectManager $manager, $id)
{
$patient = $this->getUser();
$medecin = $repoMedecin->findOneById($id);
$patient->addMedecinsFavori($medecin);
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
here's the code of the element I use to click on it to redirect on the controller previously
<fieldset>
<h4><i class="fa fa-star"></i> Dr. {{ medecin.nom }} {{ medecin.prenom }}</h4>
</fieldset>
I expected to be able to use the id of the doctor in the URL but I think when you're logged as an entity you can't get the ID (not the id used to log in) of other users and the content of the variable is empty. But I would know if there's a solution to use the ID because passing the email into the url isn't the solution I want, and I had the same problem on other piece of code where the of other entity is empty.
Here's the code of the controller that send the datas to the view
/**
* #Route("/patient/medecins-favoris", name="meet_my_doc_afficher_medecin_favoris")
*/
public function afficherMedecinFavoris()
{
$patient = $this->getUser();
$medecins = $this->getUser()->getMedecinsFavoris();
return $this->Render('meet_my_doc/afficherLesMedecinsFavoris.html.twig', ['medecins' => $medecins]);
}
try this
{{path('meet_my_doc_ajouter_medecin_favoris',{(id): medecin.id})}}
I would do something like this, first controller to receive Medicine Object like this, in this case you dont need to use $medecin = $repoMedecin->findOneById($id);
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(MedecinRepository $repoMedecin, ObjectManager $manager, Medecin $medecin)
{
$patient = $this->getUser();
$patient->addMedecinsFavori($medecin);
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
in twig in both cases you do something like this
{% for medecin in medecins %}
<fieldset>
<h4>
<i class="fa fa-star"></i>
Dr. {{ medecin.nom }} {{ medecin.prenom }}
</h4>
</fieldset>
{% endfor %}
If your code is how you've pasted it, then I think the issue lies in a typo - in the afficherMedecinFavoris() action you pass medicins to the view, and inside the view you try to access the id of medicin. So either remove the extra s in the afficherMedecinFavoris() or add that extra s to the path id parameter value.
Aside from that, I believe that this should work (under the condition that the identity field for Medecin entity is id):
/**
* #Route("/patient/ajouter-medecin-favoris/{id}", name="meet_my_doc_ajouter_medecin_favoris")
*/
public function ajouterMedecinFavoris(Medecin $medecin)
{
$patient = $this->getUser();
$patient->addMedecinsFavori($medecin);
$manager = $this->getDoctrine()->getManager();
$manager->persist($patient);
$manager->flush();
return $this->RedirectToRoute('accueil');
}
To know how the Medecin entity is available as an argument for the controller read this
I'm sorry to post the answer just now, but this how it worked for me : medecin inherit from User, so when I'm creating a medecin, it is creating also an User, both with the same Id, and if I want to use the medecin object's Id, I need to edit the getId() and replace the code by parent::getId(), this is the only way I've found to solve this problem.

What is the better solution to get the Entity I want in a biderectional OneToMany relation?

I have one entity Article and an other entity Image with a bidrectional relation OneToMany and ManyToOne :
class Article
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Image", mappedBy="article")
*/
private $images;
}
class Image
{
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Article", inversedBy="images")
* #ORM\JoinColumn(nullable=true)
*/
private $article;
}
In my controller I use #paramconverter to get the article I want :
/**
* #Route("/blog/{slug}", name="article")
* #ParamConverter("article", class="AppBundle:Article")
*/
public function articleAction(Article $article)
{
return $this->render('default/article.html.twig', array(
'article' => $article,
));
}
Now my problem is that I want to identify the ONLY image with the attributes "main = true" in all the "article.images" I have.
What is the best solution?
In my wiew I can do somehting like this but it's not the best I think :
{% for image in article.images %}
{% if image.main %}
<img src="{{ asset( image.src ) }}" alt="{{ image.alt }}" title="{{ image.title }}">
{% endif %}
{% endfor %}
I'd like to use something like :
{{ article.mainImg }}
How can I achieve this please? And is this the best solution?
Doctrine provides a collection filter mechanism you could use to get the "main image":
public function articleAction(Article $article)
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq("main", true))
->setMaxResults(1);
$mainImg = $article->getImages()->matching($criteria)->first();
return $this->render('default/article.html.twig', array(
'article' => $article,
'mainImg' => $mainImg
));
}
More information on filtering doctrine collections: Filtering collections
I did not test the code myself, but it should convey the idea of how it can be done.

Adding a search box to the homepage of a website built on Sylius

Could someone please explain how I can add a search box to the homepage of my website built on Sylius?
I have tried using the SyliusSearchBundle but that seems to be very out of data and the sample config settings throw errors.
I'd be grateful for any help.
Disclaimer
Steps below work at the time I'm writing. SyliusElasticSearchBundle plugin is in a heavy development state, some of the things crash now, but will be fixed for sure.
I've created a demo repository here: https://github.com/mheki/sylius-search-demo
You need running ElasticSearch and a SyliusElasticSearchBundle plugin.
Follow installation instructions from readme: https://github.com/Lakion/SyliusElasticSearchBundle
Just bear in mind to install sylius dev-master (circular dependency...)
Import bundle's config, routing, enable it in AppKernel.
At the moment I'm writing the bundle needed filter_sets config, otherwise it crashed
So start with the simple search by product name:
lakion_sylius_elastic_search:
filter_sets:
default:
filters:
name:
type: string
Populate elastic index with:
bin/console fos:elastic:pop
Override original routing for lakion_elastic_search_shop_product_index - use filter_set: for your channel code.
lakion_elastic_search_shop_product_index:
path: /products
methods: [GET]
defaults:
_controller: lakion_sylius_elastic_search.controller.search:filterAction
_sylius:
template: "#LakionSyliusElasticSearch/Product/index.html.twig"
resource_class: "%sylius.model.product.class%"
filter_set: default
requirements:
slug: .+
Original product index page was failing for me and I had to remove
{{ form_row(form.search) }}
from it. So copied #LakionSyliusElasticSearch/Product/index.html.twig into Resources directory:
Resources\LakionSyliusElasticSearchBundle\views\Product\index.html.twig and made that change.
Now the last thing is to create a form, for example copying the file _security.html.twig from SyliusShopBundle. Add something like this:
<div class="item">
<form action="{{ path('lakion_elastic_search_shop_product_index') }}" method="get">
<div class="ui icon input">
<input type="text" placeholder="{{ 'sylius.ui.search'|trans }}..." name="filter_set[name]" />
<button type="submit" class="ui button mini">
<i class="search icon"></i>
</button>
</div>
</form>
</div>
and here we go :)
Alternatively, if you don't want to use the SyliusElasticSearchBundle plugin, you can implement a simple search on your own (which search inside the product name, description and category name) :
Create a classic html search form, for example:
<form role="search" method="get" action="{{path('app_search_results') }}">
<input type="search" id="site-search" name="q"
placeholder="{{ 'walrus.search.input.placeholder'|trans }}"
aria-label="{{ 'walrus.search.aria.label'|trans }}">
<button>{{ 'walrus.search.aria.button'|trans }}</button>
</form>
Create a controller action for the route 'app_search_results' in order to get the search request sent by your search form :
<?php
namespace AppBundle\Controller;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use \Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class SearchController extends Controller
{
/**
* #var LocaleContextInterface
*/
private $locale;
/**
* #var ChannelContextInterface
*/
private $channelContext;
/**
* #var RepositoryInterface
*/
private $productRepository;
public function __construct(
LocaleContextInterface $locale,
ChannelContextInterface $channelContext,
RepositoryInterface $productRepository
)
{
$this->locale = $locale;
$this->channelContext = $channelContext;
$this->productRepository = $productRepository;
}
/**
* #Route("/search", name="app_search_results")
*/
public function searchAction(Request $request) : Response
{
$searchTerm = $request->query->get('q');
$channel = $this->channelContext->getChannel();
$localeCode = $this->locale->getLocaleCode();
$products = $this->productRepository->findByTerm($channel, $localeCode, $searchTerm);
return $this->render(
'#App/search/searchListProducts.html.twig',
[
'products' => $products
]
);
}
}
Don't forget to manually wire the dependency injected through the constructor.
Override the product repository in order to implements the findByTerm() method :
<?php
namespace AppBundle\Repository;
use Sylius\Bundle\CoreBundle\Doctrine\ORM\ProductRepository as
BaseProductRepository;
use Sylius\Component\Core\Model\ChannelInterface;
class ProductRepository extends BaseProductRepository
{
public function findByTerm(ChannelInterface $channel, string $locale, $searchTerm): array
{
$qb = $this->createQueryBuilder('p')
->addSelect('translation')
// get the translated product for the product regarding the current locale
->innerJoin('p.translations', 'translation', 'WITH', 'translation.locale = :locale')
->orWhere('translation.name LIKE :searchTerm')
->orWhere('translation.description LIKE :searchTerm')
// get the taxons of the product
->innerJoin('p.productTaxons', 'productTaxon')
->innerJoin('productTaxon.taxon', 'taxon')
// get the translated taxon
->innerJoin('taxon.translations', 'taxonTranslation', 'WITH', 'taxonTranslation.locale = :locale')
->orWhere('taxonTranslation.name LIKE :searchTerm')
->andWhere(':channel MEMBER OF p.channels')
->andWhere('p.enabled = true')
->setParameter('searchTerm', '%'.$searchTerm.'%')
->setParameter('locale', $locale)
->setParameter('channel', $channel)
->getQuery();
return $qb->getResult();
}
}
You just need now to create the page rendering the list of product (the #App/search/searchListProducts.html.twig). And to tell Sylius to use your custom HomepageController instead of the original one. Same goes for your Product Repository. This can be done inside the services.yml file of you app folder :
services:
sylius.controller.shop.homepage:
public: true
class: AppBundle\Controller\Shop\HomepageController
arguments:
- '#templating'
- '#sylius.context.locale'
- '#sylius.repository.taxon'
sylius_product:
resources:
product:
classes:
repository: AppBundle\Repository\ProductRepository

Details list in symfony2, from two bundles

I have little in the development of Symfony and I have a question: To show a list with details of two Bundles.
My project have a Owner, but can have 1 o more consultants. The Entity has been related but when the results are listed only can view the user id. How can see the user name?
I will attach a image with my entities.
Thanks,
View
Entities
I need show the name of the differents consultants in a project. These are users too. I have already related the owner of the project, but this consultant is a field in my entity.
Attachment images to reference, and the php and twig files content. See the image: Database relation, and view.
UserProjectController
public function showAction(UserProject $userProject, User $user) {
$id = $userProject->getId();
$em = $this->getDoctrine()->getManager();
$project = $em->getRepository('ProjectBundle:Project')->find($id);
$userProjects = $em->getRepository('ProjectBundle:UserProject')->findby(array('idproject' => $id, 'status' => '1'));
if (empty($userProjects)) {
return $this->redirectToRoute('userproject_new');
}
$users = $em->getRepository('UserBundle:User')->findby(array('id' => 1));
return $this->render('ProjectBundle:Userproject:show.html.twig', array(
'userProjects' => $userProjects, 'project' => $project, 'User' => $user,
));
}
Entity User
/**
* #ORM\OneToMany(targetEntity="ProjectBundle\Entity\UserProject", mappedBy="user")
*/
protected $uproject;
Entity UserProject
/**
* #ORM\ManyToOne(targetEntity="ProjectBundle\Entity\Project", inversedBy="userproject")
* #ORM\JoinColumn(name="project_id", referencedColumnName="id")
* #Assert\NotBlank()
*/
protected $idproject;
/**
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="uproject")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* #Assert\NotBlank()
*/
protected $user;
Entity Project
/**
* #ORM\OneToMany(targetEntity="ProjectBundle\Entity\UserProject", mappedBy="idproject")
*/
protected $userproject;
View show.html.twig
{% for userProject in userProjects %}
<br>
<dl>
<dt><span class="text-primary">{{'User_id'|trans}}</span></dt>
<dd>
{{ userProject.user }}
</dd>
<br>
<dt><span class="text-primary">{{'Consultor_id'|trans}}</span></dt>
<dd>
{{ userProject.consultorId }}
</dd>
<br>
<dt><span class="text-primary">{{'Status'|trans}}</span></dt>
<dd>
{% if userProject.status == 1 %}
<span class="text-success">{% trans %}Enabled{% endtrans %}</span>
{% elseif userProject.status == 0 %}
<span class="text-danger">{% trans %}Disabled{% endtrans %}</span>
{% endif %}
</dd>
<br>
</dl>
{% endfor %}

Resources