Error with 'orderby' column not found - symfony

I have this very simple method in my repository class which fetches a list as query builder object:
public function fetchListAsQueryBuilder(User $user, $receiverType, $limit, $offset)
{
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
$query = $queryBuilder
->select(['no'])
->from('SLCoreBundle:Notification', 'no')
->where('no.receiver = :user')
->andWhere('no.receiverType = :receiverType')
->orderBy('no.createdAt', 'DESC')
->setParameters([
'user' => $user,
'receiverType' => $receiverType,
])
->setMaxResults($limit)
->setFirstResult($offset)
;
return $query;
}
this method works perfectly in my prod server, but gives an error in my local machine, php versions are same(5.5.9), here is an error:
An exception occurred while executing 'SELECT DISTINCT id_6 FROM
(SELECT s0_.receiver_type AS receiver_type_0, s0_.importance AS
importance_1, s0_.seen AS seen_2, s0_.deleted AS deleted_3,
s0_.created_at AS created_at_4, s0_.updated_at AS updated_at_5, s0_.id
AS id_6, s0_.reason AS reason_7 FROM sl_notification s0_ WHERE
s0_.receiver_id = ? AND s0_.receiver_type = ?) dctrn_result ORDER BY
s0_.created_at DESC LIMIT 25 OFFSET 0' with params [2, 1]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 's0_.created_at' in 'order clause'
My entity has been configured like this:
Mapped superclass AbstractMessage:
abstract class AbstractMessage
{
use CreatedUpdatedAtTrait;
// here go properties, setters and getter
Notification class:
class Notification extends AbstractMessage
{
// here go properties, setters and getters
And CreateUpdatedAtTrait:
trait CreatedUpdatedAtTrait
{
/**
* #var \DateTime
*/
private $createdAt;
/**
* #var \DateTime
*/
private $updatedAt;
// Here go setters and getters
}
Schema (AbstractMessage) :
<mapped-superclass name="SL\CoreBundle\Entity\AbstractMessage">
...
<field name="createdAt" column="created_at" type="datetime">
<gedmo:timestampable on="create" />
</field>
<field name="updatedAt" column="updated_at" type="datetime">
<gedmo:timestampable on="update" />
</field>
</mapped-superclass>
here is the db table:
I'dont understand what causes this error, my others entities work well with this trait, and also my other queries with orderBy method and mappedsuperclass classes work without any error. And also very interesting part is if I remove orderBy my method is working and I am able to get the createdAt value ($object->getCreatedAt()). Can anyone help me to solve this problem?
Edit: I forgot to mention, that I've recently updated vendors to the latest versions(sf-2.6.6, DoctrineORM-2.5.0).

I think it's cause by doctrine 2.5 +
So i switched back to 2.4.7 and its working again !
In my composer.json
"require": {
...
"doctrine/orm": "2.4.7",
...
}
And update
php composer.phar update

Related

symfony querybuilder for search by relation in collection

I have Entity Application with relation to Applicant
/**
* #ORM\ManyToOne(targetEntity=Applicant::class, inversedBy="applications")
* #ORM\JoinColumn(nullable=false)
*/
private $applicant;
now I try create QueryBuilder for search application by Applicant name in ApplicantRepository i have
public function searchByName($searchString)
{
return $this->createQueryBuilder('a')
->andWhere('a.name LIKE :phrase')->setParameter('phrase', '%'.$searchString.'%')
->getQuery()
->getResult();
}
in controller I have
$applicants = $applicantRepository->searchByName($searchString);
Now I want search Application with applicant name in this applicants collection. May I use QueryBuilder fot that?
I am trying something like this
public function getApprovedSearchByApplicants($applicants)
{
return $this->createQueryBuilder('a')
->andWhere('a.applicant IN (:applicants)')
->setParameter('applicants', $applicants)
->getQuery()
->getResult();
}
so, looking to your configuration, your Application::$applicant === Applicant::$name, just because Application::$applicant property has Applicant::$id value, by default. You can check the documentation.
So, this way, you need to make smth like this:
/**
* #ORM\ManyToOne(targetEntity=Applicant::class, inversedBy="applications")
* #ORM\JoinColumn(name="applicant_name", referencedColumnName="name", nullable=false)
*/
private $applicant;
It should work.
UPDATE after question update and discussions:
So, the problem was in the testing data in the database. Bad question.
I did not test it, but something like the following code should do the trick. It is almost the same solution as goulashsoup proposed, but without typing raw DQL.
/**
* #param array|Applicant[] $applicants
*
* #return array|Application[]
*/
public function findByApplicants(array $applicants): array
{
$qb = $this->createQueryBuilder('a')
return $qb->innerJoin('a.applicant', 'at')
->where(
$qb->expr()->in('at.id', ':applicants')
)
->setParameter('applicants', $applicants)
->getQuery()
->getResult();
}
I don't think you need to name the function wtih "ApprovedSearch" since the method is only aware of a list of Applicant for whom you want the list of Application.
Search by search string:
$entityManager
->createQuery('
SELECT ct
FROM App\Entity\Application ct
JOIN ct.applicant nt
WHERE nt.name LIKE :phrase
')
->setParameters(['phrase' => "%$searchString%"])
->getResult();
Search by applicants:
$entityManager
->createQuery('
SELECT ct
FROM App\Entity\Application ct
JOIN ct.applicant nt
WHERE nt IN (:nts)
')
->setParameters(['nts' => $applicants])
->getResult();

Doctrine ArrayCollection Criteria with OneToMany Relationship

I have a OneToMany Unidirectional relationship between an "Employee" and "Status".
There is then also a ManyToMany biderectional relationship between "Employee" and "Documents".
When I have my a Document, i am trying to find all related employees ($Document->getEmployees()) and then "filter" (using ->matching(Criteria)) by the "Status"
I keep getting the below error:
2018-04-05T14:35:19+00:00 [error] Error thrown while running command "app:expiration-check". Message: "Notice: Undefined index: Status"
In DefaultQuoteStrategy.php line 39:
Notice: Undefined index: Status
Here is the Code i am using:
$Employees = $Document->getEmployees()->matching(
Criteria::create()
->andWhere(Criteria::expr()->eq('Status',$this->GlobalSettings->getApprovedEmployeeStatus()))
);
Interestingly enough, the exact same criteria works if i am using the Employee Repository
$Employees = $this->em->getRepository(Employee::class)->matching(
Criteria::create()
->andWhere(Criteria::expr()->eq('Status',$this->GlobalSettings->getApprovedEmployeeStatus()))
);
Matching static fields also works fine.
$Employees = $Document->getEmployees()->matching(
Criteria::create()
->andWhere(Criteria::expr()->eq('FirstName',"Keven"))
);
Here is the Status Column defintion
/**
* #ORM\ManyToOne(targetEntity="Entity\Accounts\EmployeeStatus")
* #ORM\JoinColumn(name="StatusId", referencedColumnName="id", nullable=false)
*/
private $Status;
Here is the Employees Defintion (on Document Entity)
/**
* #ORM\ManyToMany(targetEntity="Entity\Accounts\Employee", mappedBy="Documents")
*/
private $Employees;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->Employees = new \Doctrine\Common\Collections\ArrayCollection();
}
and Here is the getEmployees() (also on Document)
/**
* Get employees.
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEmployees()
{
return $this->Employees;
}
To manage ManyToMany relations, doctrine uses Doctrine\ORM\Persisters\Collection\ManyToManyPersister class.
You can see it being used here
Unfortunately, currently in the latest release, v2.6.1, method loadCriteria of this class is lacking the feature to use relation fields. Only static fields are supported.
Looking at the master branch currently, this support has been added: Doctrine\ORM\Persisters\Collection\ManyToManyPersister as of today
but it is not part of a release yet. Also having a quick look at 2.7 branch it does not look it will be there.
I am not sure whether you could use the master branch with symfony `s doctrine bundle. I think it will be difficult to get this to work now.
What you could do, is initialize the ManyToMany collection $Document->getEmployees() and then use the matching function, which means that you load all employees and then filter, not lazy load as you would expect.
So do:
$employees = $Document->getEmployees();
$employees->initialize();
$employees->matching(
Criteria::create()
->andWhere(Criteria::expr()->eq('Status',$this->GlobalSettings->getApprovedEmployeeStatus()))
);
and put a note to change the code, when the new changes are released.

Key "productTitle" for array with keys "0, catTitle" does not exist in EagleShopBundle:global:product.html.twig

I'm trying to join two tables and print a value in twig template but I'm having this issue.
This is my Controller action.
/**
* #Route("products/display/{id}")
* #Template()
*/
public function displayAction($id) {
$em = $this->container->get('doctrine.orm.entity_manager');
$qb = $em->createQueryBuilder();
$qb->select('p, pc.catTitle')
->from('EagleShopBundle:Products', 'p')
->leftJoin('EagleShopBundle:ProductCategory', 'pc', \Doctrine\ORM\Query\Expr\Join::WITH, 'pc.id = p.category')
->where($qb->expr()->eq('p.id', '?5'))
->setParameter(5, $id);
$product = $qb->getQuery()->getOneOrNullResult();
return $this->render("EagleShopBundle:global:product.html.twig", array(
'product' => $product,
'image_path' => '/bundles/eagleshop/images/'
));
}
This is my twig file line related to the issue,
<h1>{{product.productTitle}}</h1>
I guess issue is related to this line
$qb->select('p, pc.catTitle')
This is the error I get,
Key "productTitle" for array with keys "0, catTitle" does not exist in
EagleShopBundle:global:product.html.twig
You could try next query:
$qb->select('p, partial pc.{id, catTitle}')
// if you need full productCategory object then write just 'p, pc'
->from('EagleShopBundle:Products', 'p')
->leftJoin('p.category', 'pc')
//productCategory is the field
//in product entity which has relation to product category entity,
//paste your field (not column!) name here
//if it is not productCategory
->where('p.id = :productId')
->setParameter('productId', $id);
P.S.
It is better to move queries to entity repositories :)
P.P.S.
Doctrine partial objects
UPD
Fixed query - with right field name

OneToMany relationship not persisting along with new entities

I'm facing some issue when I try to persist a collection of entities using a symfony form. I followed the official documentation but I can't make it work becouse of this error:
Entity of type ProductItem has identity through a
foreign entity Product, however this entity has no identity itself. You have to call
EntityManager#persist() on the related entity and make sure that an identifier was
generated before trying to persist ProductItem. In case of Post Insert ID
Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you
have to call EntityManager#flush() between both persist operations.
I have to entities linked with a OneToMany relation:
Product
/**
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="ProductItem", mappedBy="product",cascade={"persist"})
*/
protected $items;
And ProductItem
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="Product", inversedBy="items")
*/
protected $product;
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="Item")
*/
protected $item;
This is how it is added to the form:
->add('items','collection',array(
'label' => false,
'type' => new ProductItemType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false))
And this is the controller action:
public function newAction()
{
$product= new Product();
$form = $this->createForm(new ProductType(), $product);
if($request->isMethod("POST"))
{
$form->handleRequest($request);
if($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
}
}
}
I'm doing something wrong for sure in the controller because, as the error message says, I have to persist $product before adding $productItems, but how can I do that?
I only get this error when trying to persist a new entity, if the entity has been persisted before, I can add as may items as I want successfully
I had exact same problem last week, here is a solution I found after some reading and testing.
The problem is your Product entity has cascade persist (which is usually good) and it first try to persist ProductItem but ProductItem entities cannot be persisted because they require Product to be persisted first and its ID (Composite key (product, item).
There are 2 options to solve this:
1st I didn't use it but you could simply drop a composite key and use standard id with foreign key to the Product
2nd - better This might look like hack, but trust me this is the best what you can do now. It doesn't require any changes to the DB structure and works with form collections without any problems.
Code fragment from my code, article sections have composite key of (article_id, random_hash). Temporary set one to many reference to an empty array, persist it, add you original data and persist (and flush) again.
if ($form->isValid())
{
$manager = $this->getDoctrine()->getManager();
$articleSections = $article->getArticleSections();
$article->setArticleSections(array()); // this won't trigger cascade persist
$manager->persist($article);
$manager->flush();
$article->setArticleSections($articleSections);
$manager->persist($article);
$manager->flush();
You didn't follow the docs completely. Here is something you can do to test a single item, but if you want to dynamically add and delete items (it looks like you do), you will also need to implement all the javascript that is included in the docs that you linked to.
$product= new Product();
$productItem = new ProductItem();
// $items must be an arraycollection
$product->getItems()->add($productItem);
$form = $this->createForm(new ProductType(), $product);
if($request->isMethod("POST"))
{
$form->handleRequest($request);
if($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($productItem);
$em->persist($product);
$em->flush();
}
}
So this should work for a single static item, but like I said, the dynamic stuff is a bit more work.
The annotation is wrong... the cascade persist is on the wrong side of the relation
/**
* #ORM\OneToMany(targetEntity="ProductItem", mappedBy="product")
*/
protected $items;
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="Product", inversedBy="items", cascade={"persist"})
*/
protected $product;
Another way to achieve this (e.g. annotation not possible) is to set the form by_reference
IMO, your problem is not related to your controller but to your Entities. It seems your would like to make a ManyToMany between your Product and Item and not creating a ProductItem class which should behave as an intermediate object for representing your relation. Additionally, this intermediate object have no id generation strategy. This is why Doctrine explains you, you must first persist/flush all your new items and then persist/flush your product in order to be able to get the ids for the intermediate object.
Also faced this issue during the work with form to which CollectionType field was attached. The other one approach which could solve this problem and also mentioned in doctrine official documentation is following:
public function newAction()
{
$product= new Product();
$form = $this->createForm(new ProductType(), $product);
if($request->isMethod("POST"))
{
$form->handleRequest($request);
if($form->isValid())
{
foreach ($product->getItems() as $item)
{
$item->setProduct($product);
}
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
}
}
}
In simple words, you should provide product link to linked items manually - this is described in "Establishing associations" section of following article: http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#working-with-associations

Unable to deal with MySQL column of type "SET" in Sonata Admin

I have a column in MySQL table defined as follows:
`fuel_type` set('gasoline','diesel','LPG','CNG','ethanol','bio-diesel','hydrogen') DEFAULT NULL,
I generated entities usingn doctrine's database introspection feature. The generated code in the entity in question is this:
/**
* #var simplearray
*
* #ORM\Column(name="fuel_type", type="simplearray", nullable=true)
*/
private $fuelType;
/**
* Set fuelType
*
* #param \simplearray $fuelType
* #return NomEngine
*/
public function setFuelType(\simplearray $fuelType)
{
$this->fuelType = $fuelType;
return $this;
}
/**
* Get fuelType
*
* #return \simplearray
*/
public function getFuelType()
{
return $this->fuelType;
}
In my sonata admin class the configureFormsFields method is thefined this way:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add('fuel_type', 'choice', array(
'choices' => array(
'gasoline' => 'Gasoline',
'diesel' => 'Diesel',
'LPG' => 'LPG',
'CNG' => 'CNG',
'ethanol' => 'Ethanol',
'bio-diesel' => 'Bio Diesel',
'hydrogen' => 'Hydrogen'
),
'multiple' => true,
'required' => false
));
;
}
The problem is that after I try to save record in the database I get this exception:
Unknown column type "simplearray" requested. Any Doctrine type that you use has to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database introspection then you might have forgot to register all database types for a Doctrine Type. Use AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement Type#getMappedDatabaseTypes(). If the type name is empty you might have a problem with the cache or forgot some mapping information.
500 Internal Server Error - DBALException
I tried a couple of things to resolve this issue:
I noticed, that the generated type is 'simplearray', but in doctrine this type is 'simple_array'. I thought there was a typo.
Without success I tried to map simplearray to simple_array in config.yml :
doctrine:
dbal:
mapping_types:
simplearray: simple_array
After that I tried to change simplearray to simple_array in the entity. I got this error:
Catchable Fatal Error: Argument 1 passed to Acme\AdminBundle\Entity\Engine::setFuelType() must be an instance of simple_array, array given,
I thought that the admin class was passing array, and the entity was expecting simple_array, so I changed simple_array to array in the entity.
Now the error was this:
Could not convert database value "" to Doctrine Type array 500 Internal Server Error - ConversionException
Any insights about dealing with set columns in Sonata Admin will be greatly appreciated!
Your entity setter & getter are wrong too and should deals with a PHP array as Doctrine is converting it, I think you must change them to:
/**
* Set fuelType
*
* #param array $fuelType
*
* #return NomEngine
*/
public function setFuelType(array $fuelType)
{
$this->fuelType = $fuelType;
return $this;
}
/**
* Get fuelType
*
* #return array
*/
public function getFuelType()
{
return $this->fuelType;
}
It seems that Doctrine doesn't handle well the set syntax in MySQL. I see 2 ways you could solve your issue:
Change your MySQL schema to put in a json array or a varchar instead of your set. Probably the fastest way.
You might not have the luxury to change your schema. In that case, define a custom Doctrine type to suit your needs as described there: http://docs.doctrine-project.org/en/2.0.x/cookbook/mysql-enums.html#solution-2-defining-a-type ; you'll need then to register it to Symfony as explained there: http://symfony.com/doc/current/cookbook/doctrine/dbal.html#registering-custom-mapping-types
Hope it helps!

Resources