I'm trying to display some fields of a table 'Post' using a raw query :
$connection = $em->getConnection();
$statement = $connection->prepare("SELECT * FROM Post WHERE category_id = 1");
$statement->execute();
$posts = $statement->fetchAll();
...
foreach($posts as $post) {
$xml .= $post->getId();
$xml .= $post->getTitle();
$xml .= $post->getContent();
}
I've got an error "FatalErrorException: Error: Call to a member function getId() on a non-object in ..."
All those getters are right in my Post entity. Any suggestion about what I'm doing wrong ?
[EDIT]
$em = $this->getDoctrine()->getManager();
$post_repository = $em->getRepository('MyBundle:Post');
$posts = $post_repository->findBy(array('category_id' => 1));
foreach($posts as $post) {
$xml .= $post->getTitle();
}
Returns me "Unrecognized field: category_id".
My Post class :
class Post
{
/**
* #ORM\ManyToOne(targetEntity="MyBundle\Entity\Category", inversedBy="post")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
* })
*/
private $category;
/**
* Set category
*
#param MyBundle\Entity\Category $category
*/
public function setCategory(\MyBundle\Entity\Category $category)
{
$this->category = $category;
}
/**
* Get category
*
#return MyBundle\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
....
Why using directly your connection here? You should consider using the entity repository for your "posts" class. For example :
$posts = $em->getRepository('YourBundle:Post')->findBy(array('category_id' => 1));
this should work, just replace the YourBundle:Post with the proper bundle and class names. Same for the category_id, I can't guess without your implementation if it's the class property or the mapping name.
I suggest you to read more on the official Doctrine documentation to improve your knowledge on the subject.
When you execute a raw query using Doctrine's DBAL layer the results come back as an array of field names mapped to values rather than as entities.
Therefore you need something like this:
foreach($posts as $post) {
$xml .= $post['id'];
$xml .= $post['title'];
$xml .= $post['content';
}
Related
In my database, I have a table T and a view V.
The view has some columns of my table and other data (from other tables).
In Symfony, I declared my view as a read-only Entity.
/**
* #ORM\Table(name="V")
* #ORM\Entity(readOnly=true, repositoryClass="AppBundle\Entity\Repository\VRepository")
*/
class V
{
In my T entity, I did a Join :
/**
* #ORM\OneToOne(targetEntity="V")
* #ORM\JoinColumn(name="T_id", referencedColumnName="V_id")
*/
private $view;
And I added just the getter :
/**
* Get view
*
* #return \AppBundle\Entity\V
*/
public function getView()
{
return $this->view;
}
Everything is working well when I want to read and show data.
But I have a problem after persisting a new T entity.
Symfony seems to lost posted data of my form when I create a new T entity (editAction() works perfectly).
An exception occurred while executing 'INSERT INTO T (T_id, T_name, updated_at) VALUES (?, ?, ?)' with params [null, null, "2017-09-01 15:30:41"]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Field 'T_id' cannot be empty (null)
When I remove ORM annotations of the $view property, it creates correctly my new record T in the database.
I think the problem is due to the fact that the V entity (the record in my SQL view) will exist just after the creation of T. And when I persist/flush data in Symfony, V doesn't exist yet. They are "created" at the same time.
I tried to add Doctrine #HasLifecycleCallbacks on my T entity and the #PostPersist event on the getView() method but it doesn't change anything...
Any idea to differ the Join after the creation of the entity ?
I know it's not conventional to use views as entities with Symfony but I haven't other choice.
I've just checked, it works fine with Bidirectional One-To-One relation
In my case tables are defined like:
create table T (`id` int(11) NOT NULL AUTO_INCREMENT, name varchar(100), primary key (id));
create view V as select id as entity, name, '123' as number from T;
Annotations in T:
/**
* #ORM\Table(name="T")
* #ORM\Entity()
*/
class T
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #var V
*
* #ORM\OneToOne(targetEntity="V", mappedBy="entity")
*/
private $view;
Annotations in V:
/**
* #ORM\Table(name="V")
* #ORM\Entity(readOnly=true)
*/
class V
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="number", type="string", length=255, nullable=true)
*/
private $number;
/**
* #var T
*
* #ORM\Id
* #ORM\OneToOne(targetEntity="T", inversedBy="view")
* #ORM\JoinColumn(name="entity", referencedColumnName="id")
*/
private $entity;
And a test snippet to prove that it saves, updates and reads fine:
public function testCRUD()
{
/** #var EntityManager $manager */
$manager = $this->client->getContainer()->get('doctrine.orm.default_entity_manager');
$t = new T();
$t->setName('Me');
$manager->persist($t);
$manager->flush();
$t->setName('He');
$manager->flush();
$manager->clear();
/** #var T $t */
$t = $manager->find(T::class, $t->getId());
$this->assertEquals('He', $t->getView()->getName());
}
Based on the #Maksym Moskvychev answer : Prefer a bidirectional One-to-One relation.
T Entity :
/**
* #ORM\OneToOne(targetEntity="V", mappedBy="entity")
*/
private $view;
V Entity :
/**
* #ORM\OneToOne(targetEntity="T", inversedBy="view")
* #ORM\JoinColumn(name="V_id", referencedColumnName="T_id")
*/
private $entity;
Fix the loss of data after posting the addAction() form (new T instance).
In the form where I list all T records :
$builder->add('entity', EntityType::class, array(
'class' => 'AppBundle:T',
'choice_label' => 'id',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('t')
->orderBy('t.name', 'ASC')
->setMaxResults(25); // limit the number of results to prevent crash
}
))
Fix the too consuming resources problem (show 25 entities instead of 870+).
Ajax request :
$(".select2").select2({
ajax: {
type : "GET",
url : "{{ path('search_select') }}",
dataType : 'json',
delay : 250,
cache : true,
data : function (params) {
return {
q : params.term, // search term
page : params.page || 1
};
}
}
});
Response for Select2 :
$kwd = $request->query->get('q'); // GET parameters
$page = $request->query->get('page');
$limit = 25;
$offset = ($page - 1) * $limit;
$em = $this->getDoctrine()->getManager();
$repository = $em->getRepository('AppBundle:V');
$qb = $repository->createQueryBuilder('v');
$where = $qb->expr()->orX(
$qb->expr()->like('v.name', ':kwd'),
$qb->expr()->like('v.code', ':kwd')
);
$qb->where($where);
// get the DQL for counting total number of results
$dql = $qb->getDQL();
$results = $qb->orderBy('m.code', 'ASC')
->setFirstResult($offset)
->setMaxResults($limit)
->setParameter('kwd', '%'.$kwd.'%')
->getQuery()->getResult();
// count total number of results
$qc = $em->createQuery($dql)->setParameter('kwd', '%'.$kwd.'%');
$count = count($qc->getResult());
// determine if they are more results or not
$endCount = $offset + $limit;
$morePages = $count > $endCount;
$items = array();
foreach ($results as $r) {
$items[] = array(
'id' => $r->getCode(),
'text' => $r->getName()
);
}
$response = (object) array(
"results" => $items,
"pagination" => array(
"more" => $morePages
)
);
if (!empty($results))
return new Response(json_encode($response));
I have an entity which looks like
.
.
.
/**
* #ORM\ManyToMany(targetEntity="PrLeadBundle\Entity\Stock")
* #ORM\JoinColumn(name="stock_id", referencedColumnName="id", nullable=true)
*/
private $stock;
public function __construct()
{
$this->stock = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add stock
*
* #param \PrLeadBundle\Entity\Stock $stock
* #return ValueList
*/
public function addStock(\PrLeadBundle\Entity\Stock $stock)
{
$this->stock[] = $stock;
return $this;
}
/**
* Remove stock
*
* #param \PrLeadBundle\Entity\Stock $stock
*/
public function removeStock(\PrLeadBundle\Entity\Stock $stock)
{
$this->stock->removeElement($stock);
}
/**
* Get stock
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getStock()
{
return $this->stock;
}
Now, If I try to access theese values I get an Join Column error.
I'm using :
$Values = $entityManager->getRepository('PrLeadBundle:ValueList')->findBy(array('disabled' => '0','exportable'=>'1'), array('title' => 'ASC'));
foreach ($Values as $item){
var_dump($item->getStock()->getId());
}
This is a bit confusing for me cause i ran into an :
Attempted to call method "getId" on class
"Doctrine\ORM\PersistentCollection". 500 Internal Server Error -
UndefinedMethodException
I did thsi for certain times in past, and I can also access the stock values in twig by using {{ item.stock.id }} ...
What am I doing wrong??
Because $stock variable is a Doctrine\Common\Collections\ArrayCollection,
so instead of $item->getStock()->getId() you should try something like $item->getStock()->first()->getId()); or var_dump($item->getStock() to see the structure before extracting the element.
I use KNP Doctrinebehaviors Bundle to translate my entity, and a2lix_translations to get i18n form,
I have no problems with those steps :
Adding entity with multi-languages.
Getting entities in cases my default locale language.
Update entity.
Delete entity.
But the probleme is how to access the propreties of my Page Entity in twig?
This is somes pictures to understand the problem :
This is my PageEntity
public function findAllByLocale($locale){
return $this->createQueryBuilder('a')
->join('a.translations', 'aTrans')
->where('aTrans.locale = :locale')
->setParameter("locale", $locale)
->addSelect('aTrans')
->getQuery()
->getResult()
;
}
use ORMBehaviors\Translatable\Translation;
/**
* #var string $title
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string $content
*
* #ORM\Column(name="content", type="text")
*/
private $content;
/**
* #ORM\ManyToOne(targetEntity="Page", inversedBy="trans", cascade={"persist", "remove"})
* #var Collection
*/
private $object;
/**
* Get title
*
* #return string
*/
public function getTitle()
{
if( $title == $this->translate()->getTitle() ) {
return $title;
}
return '';
}
/**
* Set title
*
* #param string $title
* #return Page
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Set content
*
* #param string $content
* #return Page
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* #param $method
* #param $args
*
* #return mixed
*/
public function __call($method, $args)
{
if (!method_exists(self::getTranslationEntityClass(), $method)) {
$method = 'get' . ucfirst($method);
}
return $this->proxyCurrentLocaleTranslation($method, $args);
}
and this is my query :
<!-- begin snippet: js hide: true -->
FormType
twig page : index.html.twig
Thank you
Here's the answer to what I understood:
FIRST:
If you want to display the translated fields of an entity based on guessed locale, from the doc:
proxy translations
An extra feature allows you to proxy translated fields of a translatable entity.
You can use it in the magic __call method of you translatable entity so that when you try to call getName (for example) it will return you the translated value of the name for current locale:
public function __call($method, $arguments)
{
return $this->proxyCurrentLocaleTranslation($method, $arguments);
}
Now when displaying an entity that has a translated field name in a twig template you can use:
{{ entity.getName() }}
The proxy will intercept your call and return the appropriate content by guessing the locale from the request. This is how you'll want to display most entities.
SECOND:
The other way if you specifically want to translate an entity in French then you can use the following, also in twig:
{{ entity.translate('fr').getName() }}
I am trying to setup a weighted tag cloud, that works when tags are a string property in the blog entity.
Now I've setup tags as it's own entity and related to blog as a bi-directional ManyToMany/ManyToMany relationship.
Needless to say it's not working, I'm guessing it's because tag is now it's own object.
My question: What am I doing wrong here now that tags is it's own entity and not a string property from blog entity?
When I dump $tagWeights from the controller below, I get an error when I should be seeing something like this:
array (size=78)
'Tag1' => float 1
'Tag2' => float 5
'Tag3' => float 2
error: (this line:
foreach ($tags as $tag) {
$tagWeights[$tag] = (isset($tagWeights[$tag])) ? $tagWeights[$tag] + 1 : 1;
}
ContextErrorException: Warning: Illegal offset type in isset or empty in /var/www/html/Satori/src/Symfony/AcmeBundle/Entity/TagRepository.php line 34
I'm calling tags in the following way in twig:
Twig
{% for tag, weight in tags %}
<span class="weight-{{ weight }}">{{ tag }}</span>
{% else %}
<p>There are no tags</p>
{% endfor %}
Controller (dumping $tags works great shows all the tags)
public function footerAction()
{
$em = $this->getDoctrine()->getManager();
$tags = $em->getRepository('AcmeBundle:Tag')
->getTags();
$tagWeights = $em->getRepository('AcmeBundle:Tag')
->getTagWeights($tags);
var_dump($tagWeights); die();
return array(
'tags' => $tagWeights,
);
}
Here is the getTags and getTagWeights:
public function getTags()
{
$tags = $this->createQueryBuilder('t')
->select('t.tag')
->getQuery()
->getResult();
return $tags;
}
public function getTagWeights($tags)
{
$tagWeights = array();
if (empty($tags))
return $tagWeights;
foreach ($tags as $tag)
{
$tagWeights[$tag] = (isset($tagWeights[$tag['tag']])) ? $tagWeights[$tag] + 1 : 1;
}
// Shuffle the tags
uksort($tagWeights, function() {
return rand() > rand();
});
$max = max($tagWeights);
// Max of 5 weights
$multiplier = ($max > 5) ? 5 / $max : 1;
foreach ($tagWeights as &$tag)
{
$tag = ceil($tag * $multiplier);
}
return $tagWeights;
}
Tag entity
class Tag
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="tag", type="string", length=255)
*/
private $tag;
/**
* #ORM\ManyToMany(targetEntity="Blog", mappedBy="tags")
*/
protected $blogs;
public function __construct()
{
$this->blogs = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set tag
*
* #param string $tag
* #return Tag
*/
public function setTag($tag)
{
$this->tag = $tag;
return $this;
}
/**
* Get tag
*
* #return string
*/
public function getTag()
{
return $this->tag;
}
/**
* Add blogs
*
* #param \AcmeBundle\Entity\Blog $blogs
* #return Tag
*/
public function addBlog(\AcmeBundle\Entity\Blog $blogs)
{
$this->blogs[] = $blogs;
return $this;
}
/**
* Remove blogs
*
* #param \AcmeBundle\Entity\Blog $blogs
*/
public function removeBlog(\AcmeBundle\Entity\Blog $blogs)
{
$this->blogs->removeElement($blogs);
}
/**
* Get blogs
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getBlogs()
{
return $this->blogs;
}
}
Proposed solutions and results
Using (isset($tagWeights[$tag['tag']]))
error:
ContextErrorException: Warning: Illegal offset type in /var/www/html/Satori/src/Symfony/AcmeBundle/Entity/TagRepository.php line 34
Using (isset($tagWeights[$tag[$tag->getTag()]))
error:
ContextErrorException: Notice: Trying to get property of non-object in /var/www/html/Satori/src/Symfony/AcmeBundle/Entity/TagRepository.php line 34
I think I know what's happening here...
When you make this call in your footerAction:
$tags = $em->getRepository('AcmeBundle:Tag')
->getTags();
your repository returns to you an array of arrays. So when you say:
isset($tagWeights[$tag])
...that $tag is actually an associative array of the result set. I think what you mean to say here is:
isset($tagWeights[$tag['tag']])
That said, I'd advise against giving your Tag entity a property called tag. a tag doesn't have a tag, it has a name, or a description. And it'd make much more sense here to say
isset($tagWeights[$tag['name']])
...or by having the repository return the Tag entities themselves (why do you even need to override the built-in getXyz() method?), and do:
isset($tagWeights[$tag->getName()])
Hey guys I'm looking for help again.
My problem is that two related ORM entities not getting stored.
I run the following Unit test and it fails:
public function testCategoryRelation()
{
$doctrine = $this->client->getContainer()->get('doctrine');
$itemRepo = $doctrine->getRepository("AppBundle:Item");
$item = $itemRepo->find(230045);
$this->assertTrue($item instanceof \Acme\TestBundle\Entity\Item);
$categories = $item->getCategories();
$this->assertGreaterThan(0, $categories->count());
foreach ($categories as $category) {
$this->assertTrue($category instanceof \Acme\TestBundle\Entity\Category);
}
$count = $categories->count();
$categoryRepo = $doctrine->getRepository("AppBundle:Category");
$categories = $categoryRepo->findBy(array(), null, 2);
$this->assertEquals( 2, count($categories) );
foreach ($categories as $category) {
$this->assertTrue($category instanceof \Acme\TestBundle\Entity\Category);
$category->addItem($item);
$item->addCategory($category);
}
$this->assertGreaterThan($count, $item->getCategories()->count());
$em = $doctrine->getManager();
foreach ($categories as $category) {
$em->persist($category);
}
$em->persist($item);
$em->flush();
$em->clear($item);
$item = $itemRepo->find(230045);
$categories = $item->getRelatedItems();
$this->assertGreaterThan($count, $categories->count()); /* Here it fails */
}
I have no clue why the last test fails. Could you please help me find my mistake?
I run Symfony 2.1.4 together with Doctrine 2.3
I have two ORM Objects.
1. Item
namespace Acme\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="item")
*/
class Item
{
/**
* #ORM\ManyToMany(targetEntity="Category", mappedBy="items")
*/
private $categories;
public function __construct()
{
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #param \Acme\TestBundle\Entity\Category $category
*/
public function addCategory(\Acme\TestBundle\Entity\Category $category)
{
$this->categories[] = $category;
return $this;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
}
2. Category
namespace Acme\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="Acme\TestBundle\Repository\CategoryRepository")
*/
class Category
{
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Item", inversedBy="categories")
* #ORM\JoinTable(name="category_item",
* joinColumns={
* #ORM\JoinColumn(name="cat_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="item_id", referencedColumnName="id")
* }
* )
*/
private $items;
/**
* #param \Acme\TestBundle\Entity\Item $item
* #return Category
*/
public function addItem(\Acme\TestBundle\Entity\Item $item)
{
$this->items[] = $item;
return $this;
}
/**
* #return \Doctrine\Common\Collections\Collection
*/
public function getItems()
{
return $this->items;
}
}
Thank you in advance for your help!
Hm, $categories = $item->getRelatedItems();. Maybe it is better to write $categories = $item->getCategories();?
The other suggestion is to add __construct method to Category entity class and initialize $items with new ArrayCollection