How do I insert/update null values in date columns in Doctrine2?
I've set the date column to nullable=true.
/**
* #ORM\Column(type="date", nullable=true)
*/
private $dateBar;
And I've tried this:
$foo->setDateBar(new \DateTime()); // Inserts today's date
$foo->setDateBar(); // Throws error
I couldn't find anything in the doctrine2's documentation either.
I'm guessing that your setDateBar always expects a parameter? Try:
public function setDateBar($date = null)
{
$this->dateBar = $date;
}
If that is not the problem then please post your error message.
Related
I have two tables user and price_types with many to many relations that create the third table user_price_types.
I want to find all price_types for a specific user so I created this Doctrine query.
/**
* Gets User's PriceTypes Query
*
* #param User $user
* #return array
*/
public function getUserPriceTypes(User $user)
{
$qb = $this->createQueryBuilder('pt')
->innerJoin('pt.users', 'u')
->where('u.id = :user_id')
->setParameter('user_id', $user->getId());
$this->useResultCacheOnQuery($qb);
return $qb->getQuery()->getResult();
}
But I'm getting this error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'u2_.price_type_id' in 'on clause'
I agree with the error because there is no price_type_id column because the column name in the generated table is pricetype_id.
So I have two questions.
First, why is did Doctrine name the column pricetype_id instead of price_type_id?
Secondly, how do I update my query/entities to search column pricetype_id instead of price_type_id?
These are my entity statements for user and price_type, respectively:
/**
* #ORM\ManyToMany(targetEntity="ThreeWebOneEntityBundle\Entity\PriceType", mappedBy="users")
* #ORM\JoinTable(name="user_price_types")
*/
protected $priceTypes;
/**
* #ORM\ManyToMany(targetEntity="ThreeWebOneEntityBundle\Entity\User", inversedBy="priceTypes")
* #ORM\JoinTable(name="user_price_types")
*/
protected $users;
Adding the following statement to the annotation lets doctrine know what column name to look for.
joinColumns={#ORM\JoinColumn(name="pricetype_id", referencedColumnName="id
Final entity method is:
/**
* #ORM\ManyToMany(targetEntity="ThreeWebOneEntityBundle\Entity\User", inversedBy="priceTypes")
* #ORM\JoinTable(name="user_price_types",
* joinColumns={#ORM\JoinColumn(name="pricetype_id", referencedColumnName="id")})
*/
protected $users;
Let's assume i have "News" entity which has got ManyToMany "Tag" relation
class News
{
/**
* #ORM\ManyToMany(targetEntity="App\Domain\Entity\Vocabulary\Tag")
*/
private Collection $tags;
}
And i have such query:
public function getList(
array $tags = null,
): Query {
if (null !== $tags) {
$qb->andWhere('nt.id IN (:tags)');
$qb->setParameter('tags', $tags);
}
}
The problem is when i pass ["Tag1", "Tag2"] it selects news that have either the first tag or the second, but not both at the same time. How can i rewrite the query to select news which have both tags at the same time?
Some things to notice first:
For doctrine annotations it is possible to use the ::class-constant:
use App\Domain\Entity\Vocabulary\Tag;
class News
{
/**
* #ORM\ManyToMany(targetEntity=Tag::class)
*/
private Collection $tags;
}
If the $tags array is empty doctrine will throw an exception because an empty value set is invalid SQL, at least in mysql:
nt.id IN () # invalid!
Now to the problem:
With the SQL-aggregation functions COUNT and GROUP BY we can count the number of tags for all news. Together with your condition for the allowed tags, the number of tags per news must be equal to the number of tags in the tags array:
/**
* #var EntityManagerInterface
*/
private $manager;
...
/**
* #param list<Tag> $tags - Optional tag filter // "list" is a vimeo psalm annotation.
*
* #return list<News>
*/
public function getNews(array $tags = []): array
{
$qb = $this->manager
->createQueryBuilder()
->from(News::class, 'news')
->select('news')
;
if(!empty($tags)) {
$tagIds = array_unique(
array_map(static function(Tag $tag): int {
return $tag->getId();
}) // For performance reasons, give doctrine ids instead of objects.
); // Make sure duplicate tags are handled.
$qb
->join('news.tags', 'tag')
->where('tag IN (:tags)')
->setParameter('tags', $tagIds)
->addSelect('COUNT(tag) AS HIDDEN numberOfTags')
->groupBy('news')
->having('numberOfTags = :numberOfTags')
->setParameter('numberOfTags', count($tags))
;
}
return $qb
->getQuery()
->getResult()
;
}
I have a table of states/regions which has 2 fields: state and country, where the country is a reference to the country object in table of countries:
/**
* #ORM\Entity
* #ORM\Table(name="LocationStates")
*/
class LocationState
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $state_id;
public function getStateID()
{
return $this->state_id;
}
public function setStateID($state_id)
{
$this->state_id = $state_id;
}
/**
* Many States have one Country
* #ORM\ManyToOne(targetEntity="LocationCountry")
* #ORM\JoinColumn(name="country",referencedColumnName="country_id",onDelete="SET NULL")
*/
protected $country;
public function getCountry()
{
return $this->country;
}
public function setCountry($country)
{
$this->country = $country;
}
I have a single page and controller where a user adds a new state or edits the existing state.
There are the following conditions for the States:
Countries are selected from a given set of countries (part of the site engine), a state/region cannot be saved without a selected country
There are 2 fields for the state/region: select box populated on selecting a country with known states (e.g. USA, Australia, UK) - not all countries have states, so there is also a text box to enter a region for a country without states
When a state is saved, the controller should check if a state with the same name AND country already exists - if yes, then it's an error, you can't have same state names with the same country
If a new state is saved, I check if this state new or existing and if new, then I check if another state exists with the same name and country ID:
!is_object(LocationState::getByID($state_id)) && !empty(LocationState::getByState($state, $country_id))
So far so good, that works. But problems start when I edit and save an existing state and can't figure out what the logic should be.
If I do this:
is_object(LocationState::getByID($state_id)) && LocationState::getByID($state_id)->getState() != $state && !empty(LocationState::getByState($state, $country_id))
it checks if it's an existing state, if yes then it checks I changed its name, then it checks if another one with the same name and country ID exists - and this is not working, I can't get the logic right. It only works if the country doesn't change. But if I edit the state, I may want to change its country too. But the above code saves the state with the same name and same country ID as the already existing one.
Here's the getByState() which is part of class LocationState:
public static function getByState($state, $country = null)
{
$em = \ORM::entityManager();
if (is_null($country)) {
return $em->getRepository(get_class())->findOneBy(array('state' => $state));
}
else {
$qb = $em->createQueryBuilder();
return $qb->select('c')
->from('LocationState', 'c')
->leftJoin('c.country', 'j', 'WITH', 'j.country_id = c.country')
->andWhere('j.country_id = ?1')->setParameter(1, $country)
->andWhere('c.state = ?2')->setParameter(2, $state)
->getQuery()
->getResult();
}
}
Will really appreciate your help with the last bit of logic to save edited state to make sure it doesn't exist yet.
I am using Docrine 1.2 with Zend Framework and trying to save a Doctrine Collection.
I am retrieving my collection from my table class with the following code.
public function getAll()
{
return $this->createQuery('e')
->orderBy('e.order ASC, e.eventType ASC')
->execute();
}
I also have the following class to reorder the above event records.
class Admin_Model_Event_Sort extends Model_Abstract
{
/**
* Events collection
* #var Doctrine_Collection
*/
protected $_collection = null;
public function __construct()
{
$this->_collection = Model_Doctrine_EventTypesTable::getInstance()->getAll();
}
public function save($eventIds)
{
if ($this->_collection instanceof Doctrine_Collection) {
foreach ($this->_collection as $record)
{
$key = array_search($record->eventTypeId, $eventIds);
if ($key !== false) {
$record->order = (string)$key;
}
}
return $this->_saveCollection($this->_collection);
} else {
return false;
}
}
}
The _saveCollection method above is as follows
/**
* Attempts to save a Doctrine Collection
* Sets the error message property on error
* #param Doctrine_Collection $collection
* #return boolean
*/
protected function _saveCollection(Doctrine_Collection $collection)
{
try {
$collection->save();
return true;
} catch (Exception $e) {
$this->_errorMessage = $e->getMessage();
OpenMeetings_Logger_ErrorLogger::write('Unable to save Doctrine Collection');
OpenMeetings_Logger_ErrorLogger::vardump($this->_errorMessage);
return false;
}
}
The event id's in the above save method is simply an enumerated array of event id's, I am using the keys of the array to set the sort order of the events using the order field. If I do a var_dump of the collection to an array ($this->_collection->toArray()) I get the correct data. However when I attempt to save the collection I get the following error.
"SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'order = '0' WHERE eventtypeid = '3'' at line 1"
Is there anyway I can get Doctrine to expand on this error, the full SQL statement would be a start, also if anyone knows as to why this error is occuring then that would be very helpful.
Many thanks in advance
Garry
EDIT
I have modified my above code to try to work one record at a time but I still get the same problem.
public function save($eventIds)
{
foreach ($eventIds as $key => $eventId) {
$event = Model_Doctrine_EventTypesTable::getInstance()->getOne($eventId);
$event->order = (string)$key;
$event->save();
}
}
Ok I have found the problem. I was using the MYSQL reserved word order as a field name thus the error, changed it to sortOrder and the problem went away.
Hope this helps someone with a similar issue.
Garry
I have Product table which has a related table Images with a relation 1:M.
Class Product {
private Integer productId;
private String productName;
....
....
....
private List<Image> productImageList;
....
....
....
}
Class Image{
private Integer imageId;
private String imageName;
}
Class ProductLite{
private Integer productId;
private String productName;
private String imageName;
}
I am trying a JPQL query where I want to query to fetch products and the first image from the productImageList and returning a ProductLite object using the new constructor.
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<ProductLite> getAllProductLite() {
Query q = em.createQuery("SELECT NEW com.mycomp.application.entity.ProductLite(p.productId, p.productName, p.productImageList.get(0).getImageName())"
+ " from Product p"
+ " ORDER by p.productName");
List<ProductLite> prods = q.getResultList();
return prods;
}
But for some reason I am not able to get it to work. I get a NoViableException. So I tried moving the logic of getting the first image (getImage() method) to the Product Entity so in the query I could just call the getImage(). Even that does not seem to work.
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [SELECT NEW com.meera.application.entity.ProductLite(distinct p.productId, p.productName, p.getImage()) from Product p, IN(p.productImageList) pil where p.category.categoryCode = :categoryCode ORDER by p.productName ], line 1, column 52: unexpected token [distinct].
Internal Exception: NoViableAltException(23#[452:1: constructorItem returns [Object node] : (n= scalarExpression | n= aggregateExpression );])
Any help is appreciated.
First, you cannot call methods in entity class from your JP QL query. Second, to use the order of entities in list, you need persisted order.
To create column for order to the join table between image and product, you have to add
#OrderColumn-annotation to the productImageList. For example:
#OrderColumn(name = "myimage_order")
//or dont't define name and let it default to productImageList_order
#OneToMany
private List<Image> productImageList;
Then you have to modify query to use that order to choose only first image:
SELECT NEW com.mycomp.application.entity.ProductLite(
p.productId, p.productName, pil.imageName)
FROM Product p JOIN p.productImageList pil
WHERE INDEX(pil) = 0
ORDER by p.productName