I have Eloquent Event model, which is related towards multiple dates like this:
$event->dates // shows Collection of 8 Eloquent date models
After that i need to pick the only date, what is closest to current time. I know how to do this using query of raw SQL, or DB class. But isnt there any better solution? I dont want to jump into database for data, I already have.
Date format in eloquent models is surprisingly string.
You can use what we call in laravel mutators like this ->
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
public function dates()
{
return $this->hasMany('Date');
}
/**
* Get Dates for the event.
*
* #param string $value
* #return array
*/
public function getDates()
{
$dates = $this->dates()->getQuery()->orderBy('created_at', 'asc')->get();
return $dates;
}
}
Hope this helps.
UPDATE
I think now you can also directly do this in the model definition like this -
return $this->hasMany('Date')->orderBy('created_at', 'asc')
I need to get all rows where DATE(a.when) matches the string 2014-09-30.
$builder = $this->em->createQueryBuilder();
$builder->select('a')
->from('Entity\Appointment', 'a')
->andWhere('a.when = :date')
->setParameter('date', $date);
a.when is a full DATETIME; :date is only a string (in DATE format).
The following and variations didn't work:
->andWhere('DATE(a.when) = :date')
Error: Expected known function, got 'DATE'
What's the correct usage here?
Thanks to andy, using this now:
$builder = $this->em->createQueryBuilder();
$builder->select('a')
->from('Entity\Appointment', 'a')
->andWhere('a.when >= :date_start')
->andWhere('a.when <= :date_end')
->setParameter('date_start', $date->format('Y-m-d 00:00:00'))
->setParameter('date_end', $date->format('Y-m-d 23:59:59'));
This actually is a very common question.
It turns out that not all sql databases support a DATE function, so the good people in charge of Doctrine decided not to support it nativelly.
Kind of wish they did because it would have saved a bunch of people a fair amount of effort.
So add this rather magical class to your project:
namespace Cerad\Bundle\CoreBundle\Doctrine\DQL;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
class Date extends FunctionNode
{
public $date;
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return "DATE(" . $sqlWalker->walkArithmeticPrimary($this->date) . ")";
}
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->date = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
Then wire it up in the doctrine section of your app/config.yml:
doctrine:
orm:
default_entity_manager: default
auto_generate_proxy_classes: %kernel.debug%
entity_managers:
default:
connection: default
...
dql:
datetime_functions:
date: Cerad\Bundle\CoreBundle\Doctrine\DQL\Date
http://doctrine-orm.readthedocs.org/en/latest/cookbook/dql-user-defined-functions.html
http://symfony.com/doc/current/cookbook/doctrine/custom_dql_functions.html
http://symfony.com/doc/current/reference/configuration/doctrine.html
There are other bundles out there with more sql functions. Oddly enough, the first time I looked a few years ago, none of them had Date defined. So I just made my own.
====================================================================
Update 01
I did not check the tags carefully and assumed that this was a Symfony 2 application. The Date class stays the same. You wire it up by getting the doctrine configuration object.
$config = new \Doctrine\ORM\Configuration();
$config->addCustomDatetimeFunction('DATE', 'blahblah\Date');
Check the Doctrine link for details.
A different approach using $qb->expr()->between() in the same andWhere:
$builder = $this->em->createQueryBuilder(); $builder->select('a')
->from('Entity\Appointment', 'a')
->andWhere($qb->expr()->between('a.when', ':date_start', ':date_end'))
->setParameter('date_start', $date->format('Y-m-d 00:00:00'))
->setParameter('date_end', $date->format('Y-m-d 23:59:59'));
Building a JSON response for an API type thing, to retrieve a specific set of data that includes a ManyToOne relationship in the entity for my entity that extends FOSUSerBundle's User entity (called Account in my case).
The problem is, the Account entity thats included as a field in the response, is wanted, but I dont want to include all of the password and role type stuff.
I've been browing the internet for a couple hours now, and I've followed many guides on this, and I've cleared my cache every single time, and to no avail; So here's where I ended up:
// app/config/config.yml
jms_serializer:
metadata:
auto_detection: true
directories:
FOSUserBundle:
namespace_prefix: "FOS\\UserBundle"
path: "%kernel.root_dir%/Resources/serializer/FOS"
I've for below I've tried User.Model.yml and Model.User.yml and User.Entity.yml as well in a vain thought that the file name actually matters
// app/Resources/serializer/FOS/Entity.User.yml
FOS\UserBundle\Model\User:
exclusion_policy: ALL
properties:
id:
expose: true
and what I get still looks like this:
{
"status":"ok",
"api_version":"1.0",
"code":200,
"data":{
"video":{
"id":1,
"published":true,
"visibility":true,
"title":"Megaman 2",
"slug":"megaman-2",
"summary":"A rap song about Megaman",
"description":"A rap song\r\nAbout megaman",
"youtube_id":"R6L9bUouDr8",
"date_published":"2014-07-02T14:09:26-0700",
"date_created":"2014-07-02T14:09:26-0700",
"date_updated":"2014-07-02T14:09:26-0700",
"author_id":3,
"author":{
"id":3,
"username":"kharrison",
"username_canonical":"kharrison",
"email":"(sorry private)",
"email_canonical":"(sorry, private)",
"enabled":true,
"salt":"(sorry, private)",
"password":"(sorry, private)",
"last_login":"2014-07-04T15:17:34-0700",
"locked":false,
"expired":false,
"roles":[
"ROLE_SUPER_ADMIN"
],
"credentials_expired":false,
"display_name":"Kyle Harrison",
"slug":"kyle-harrison",
"bio":"Test"
}
}
}
}
The "author" field, is my Account entity thats being run through the JMSSerializer
I want to exclude ALL of that, except the user ID, Display name, and slug.
And finally this is how the API works:
// My/Bundle/Controller/BaseAPIController.php
//......... other code
/**
* #param string $status
* #param integer $code
* #return Response
*/
public function render_api($status, $code)
{
$this->apiResponse->setStatus($status);
$this->apiResponse->setCode($code);
return new Response($this->apiResponse->serialize($this->get('jms_serializer')), $this->apiResponse->getCode(), ["Content-type"=>"application/json"]);
}
//............. other code
and finally, that calls this:
// My/Bundle/Models
class APIResponse {
protected $status;
protected $apiVersion;
protected $code;
protected $data;
public function __construct($apiVersion, $status = "OK", $code = 500)
{
$this->status = $status;
$this->code = $code;
$this->apiVersion = $apiVersion;
$this->data = [];
}
// ... getters and setters
/**
* #return mixed
*/
public function serialize($serializer) {
return $serializer->serialize($this, "json");
}
}
I've for below I've tried User.Model.yml and Model.User.yml and
User.Entity.yml as well in a vain thought that the file name actually
matters.
It does matter, actually. It's a concatenation of the namespace and class name. In this case, you're trying to configure the FOS\UserBundle\Model\User class, so the file name should be Model.User.yml. (FOS\UserBundle\ should be excluded from the file name, since you configured it as namespace_prefix in your config.yml)
Also make sure that your Account class doesn't re-declare (overwrite) the properties, as the serializer config only works if you configure it for the class that actually declares the properties.
Ok So, the actual answer, couldn't have been arrived to via the information I provided. But Nic's Answer did lead me towards the solution. The description of how the the serializer looks at and deciphers the config file lead me to the real problem at hand.
This is what I failed to show:
<?php
namespace [PRIVATE]\[PRIVATE]Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Account
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="[PRIVATE]\[PRIVATE]Bundle\Entity\AccountRepository")
*/
class Account extends BaseUser
{
The problem lays with the Alias I provided the FOS\UserBundle\Model\User namespace. I no longer remember why I wrote that that way. However, the moment I remove the Alias and rewrote the extends to resemble this instead:
<?php
namespace [PRIVATE]\[PRIVATE]Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Account
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="[PRIVATE]\[PRIVATE]Bundle\Entity\AccountRepository")
*/
class Account extends User
{
combined with the new correct filename from Nic's answer, the config based Exclusion policy for JMSSerializerBundle totally kicks in, and every instance of FOSUserBundle's items are now completely hidden, except for the fields I've now explicitly told it to expose.
This is exactly what I wanted :)
Thanks everyone for your help! Cheers
~k
I'm not sure it's the exact way you want it, more a way around:
way around 1: Select only the properties you want (via the entity manager) and then serialize the array obtained.
It's what I do with what I call my API (which is not a class as you but controllers)
I need to use a REST service in order to get some data to a plugin. In order to do so, I have overriden the normal backend interface in typoscript with the following command :
objects.Tx_Extbase_Persistence_Storage_BackendInterface.className = Tx_extensionname_Persistence_Storage_RestBackend
This BackendInterface then returns Query Objects in my repository when I use to following:
Ex:
$query = $this->createQuery();
$query = $query->execute()->toArray();
Here, $query holds the response from the service as a TYPO3 Tx_Extbase_Persistence_QueryInterface object.
The problem is that I need to be able to do a call to the service while passing an ID parameter (appending to the endpoint with /ID). Ideally, I would do it in such a way that this repo function (called in the controller) would return what I want :
public function findById( $id ) {
$query = $this->createQuery();
$query->matching($query->equals('id', $id));
return $query->execute()->toArray();
}
The problem is that I need to be able to access the query constraint within my Tx_extensionname_Persistence_Storage_RestBackend. Normally, I would use the '$query->getConstraint()' method. However, we are using typo3 4.5 and this function is not yet defined for Tx_Extbase_Persistence_QueryInterface.
Modifying the typo3 core to add this function is not an option.
I tried to extend the Query Interface to add this functionnality in a subclass in order to then override the class in typoscript but then realized this wouldn't be portable enough. I need to be able to access the query constraint only using typo3 4.5 native functionnalities.
Well I fixed it. The only thing needed to do was :
Tx_Extbase_Persistence_QueryInterface.className = Tx_MyExtension_Persistence_RestQuery
class Tx_MyExtension_Persistence_RestQuery extends Tx_Extbase_Persistence_Query implements Tx_MyExtension_Persistence_RestQueryInterface
{
}
interface Tx_MyExtension_Persistence_RestQueryInterface extends Tx_Extbase_Persistence_QueryInterface {
public function getConstraint();
}
The MongoDB documentation on Object IDs recommends using custom keys in a certain case:
If your document has a natural primary key that is immutable we recommend you use that in _id instead of the automatically generated ids.
How can I define a simple model object that does exactly this?
<?php
namespace Acme\HelloWorld\Model;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class KindWord {
/**
* #MongoDB\Id(strategy="NONE")
* #var string
*/
private $word;
public function getWord() {
return $this->word;
}
public function setWord($word) {
$this->word = $word;
}
}
Just make sure you set $word before calling persist().