So , i'm working on a news portal , and i have two entities :
News Entity (id,title...source)
Source Entity(id,title...)
In News Entity,field named 'Source' is an id to Source Entity;
So , i need to access Source Title when publishing news . I know there's posibility doing thing relationships ManyToOne . But i don't want do it , because it makes my life harder in some cases . Is there posibility to execute a Query in Entity ? like :
public function getSourceTitle()
{
$query = ...;
return $query->getScalarResult();
}
Can i do it ? or is there another posibility ?
It's not a good practice to query insiide Entity class. Entity class should hold just fields, setters and getters, nothing else. So, if you want to query the db, and how are you planning to do it? Injecting EntityManager? Ok, If you really want to do it you can simply do it like this:
...
private $entityManager;
public function __construct(EntityManagerINterface $entityManager)
{
$this->entityManager = $entityManager;
}
...
public function getSourceTitle()
{
$sourceRepository = $this->entityManager->getRepository('VendorMyBundle:Source');
$queryBuilder = $sourceRepository->createQueryBuilder('s')
->....
return $queryBuilder->getScalarResult();
}
and use it as new MyEntity($entityManager) on entity creation.. But, you should know that this is considered as very bad practice...
Related
I've just started working with Doctrine and built a simple blog project. One of my requirements is that a blog post should not be visible to anybody (for simpleness, skip an editor's interface) until the publish date is reached.
As far as I see, it's obvious to do so using a custom repository. Let's extend the find method the following way:
public function find($id, $lockMode = null, $lockVersion = null)
{
/** #var Post $post */
$post = parent::find($id, $lockMode, $lockVersion);
if($post->getCreatedAt() > new \DateTime()) {
return null;
}
return $post;
}
This restricts the access for a page showing a single Post entity. For an overview page, the same can be done using a custom method:
public function findForOverview()
{
$query = $this->createQueryBuilder('p')
->where('p.createdAt < CURRENT_TIMESTAMP()')
->orderBy('p.createdAt', 'DESC')
->getQuery();
return $query->getResult();
}
So, even for this simple requirement, I've already written two custom methods. If I continue to work on my project, other restriction limitations might occur and additional ways to load that entity might arise. And as far as I see, for each case I have to implement the logic for all access guards.
Is there no simpler way to do that? I'm thinking of something like an annotation or an "entity load listener" that makes it simple to write one single entry point for all such checks - making it impossible to forget such checks...
Such restrictions are usually implemented by using mechanism of SQL filters in Doctrine. Implementation of this filter works on lower level then DQL and allows you to apply modifications for SQL query being constructed. In your case it may look like this:
namespace App\ORM\Filter;
use App\Entity\Post;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
class PostVisibilityFilter extends SQLFilter
{
/**
* Gets the SQL query part to add to a query.
*
* #param ClassMetadata $targetEntity
* #param string $targetTableAlias
* #return string The constraint SQL if there is available, empty string otherwise
*/
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
{
if ($targetEntity->name !== Post::class) {
return '';
}
return sprintf('%s.%s >= now()', $targetTableAlias, $targetEntity->getColumnName('createdAt'));
}
}
I am using Symfony version 2.7.6. I have created an entity named EmployeeBasicInfo having fields
firstname
lastname
identificationCode etc
I have created a callback function for validating Identification code in EmployeeBasicInfo entity itself which looks like
/**
* #Assert\Callback(groups={"edit_myinfo"})
*/
public function validateIdentificationCode(ExecutionContextInterface $context)
{
if ($this->getEmployeeFirstName() == 'fakename') {
$context->buildViolation('This name sounds totally fake!')
->atPath('employeeFirstName')
->addViolation();
}
}
and this callback function works properly
Actually I want such a callback functionality which checks identidfication code against database. I have added $em = $this->getDoctrine()->getManager(); inside the callback function and the error is like Attempted to call an undefined method named "getDoctrine" of class "XXX\EmployeeBundle\Entity\EmployeeBasicInfo".. Please advise me the effective way
Do not inject the EntityManager in your Entity. One basic concept of the DataMapper-Pattern is, that your entity does not have to know about your data source and its connectors.
I'd suggest to write a custom validation constraint, in which you inject the dependencies you need.
EntityManager, Repository to query, etc. Whatever service suits you.
Have a look at how to create custom constraint validators with dependencies
I would suggest you use a service to do this
class EmployeeUtility($connection)
{
public function __construct($conn) { $this->connection = $v; }
public function validateIdentificationCode($emloyeeId, $validationCode)
{
// Your code here
}
}
In your controller, you inject the service:
$employeeUtility = $this->get('employee.utility');
$employeeUtility->validateIdentificationCode(1,'GF38883dkDdW3373d');
Alternatively, add the code in a repository class.
I'm a bit of noob when it comes to OOP PHP, so please forgive me if I make this sound more complicated then it is.
Basically I am trying to clean up my controller as it's starting to get too cluttered.
I have my entities set up and I have also created a repository to add methods for some db queries to a sqlite database.
But now I also have to manipulate this data before outputting it, I've created a separate connector class that fetches additional info (from an XML web source) for each item being queried and then this gets added to the doctrine query data before being outputted.
I could manipulate this data in the repository but the data I am adding obviously doesn't originate from my entity. So I have therefore created a separate model class to add this data.
Please tell me if I'm on the right track.
In my entity repository I will have a custom method like this:
public function queryTop10All()
{
$query = $this->getEntityManager($this->em)
->createQueryBuilder('u')
->select('u.ratingkey, u.origTitle, u.origTitleEp, u.episode, u.season, u.year, u.xml, count(u.title) as playCount')
->from($this->class, 'u')
->groupBy('u.title')
->orderBy('playCount', 'desc')
->addOrderBy('u.ratingkey', 'desc')
->setMaxResults(10)
->getQuery();
return $query->getResult();
}
Now I created a new class in \Model\ChartsDataModel.php and I am injecting doctrine into it using a service and calling the custom method, getting the results and then adding the additional data from the web connector to it, like so:
namespace PWW\DataFactoryBundle\Model;
use Doctrine\ORM\EntityManager;
use PWW\DataFactoryBundle\Connector\XMLExtractor;
use PWW\DataFactoryBundle\Connector\WebConnector;
use PWW\ContentBundle\Entity\Settings;
class ChartsDataModel {
private $settings;
private $repository;
private $em;
public function __construct(EntityManager $em)
{
$this->settings = new Settings();
$this->repository = $this->settings->getGroupingCharts() ? 'PWWDataFactoryBundle:Grouped' : 'PWWDataFactoryBundle:Processed';
$this->em = $em;
}
public function getChartsTop10All()
{
$xmlExtractor = new XMLExtractor();
$webConnector = new WebConnector();
$results = $this->em->getRepository($this->repository)->queryTop10All();
$xml = $xmlExtractor->unXmlArray($results);
$outputArray = array();
foreach($xml as $item) {
$outputArray[] = array(
"ratingKey" => $item['ratingkey'],
"origTitle" => $item['origTitle'],
"origTitleEp" => $item['origTitleEp'],
"playCount" => $item['playCount'],
"episode" => $item['episode'],
"season" => $item['season'],
"year" => $item['year'],
"type" => $item['media']['type'],
"parent" => $webConnector->getMetaData($webConnector->getMetaDataParentKey($item['ratingkey'])),
"metadata" => $webConnector->getMetaData($item['ratingkey'])
);
}
return $outputArray;
}
}
The xmlExtractor class is used to pull out certain xml fields stored in a database field as a raw xml dump.
My config.yml:
services:
pww.datafactorybundle.model.charts_data_model:
class: PWW\DataFactoryBundle\Model\ChartsDataModel
arguments: [ #doctrine.orm.entity_manager ]
Then in my controller, I just instantiate a new ChartsDataModel and call the method like so:
namespace PWW\ContentBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
...
use PWW\DataFactoryBundle\Model\ChartsDataModel;
public function chartsAction()
{
$charts = new ChartsDataModel($this->getDoctrine()->getManager());
$top10Array = $charts->getChartsTop10All();
return $this->render('PWWContentBundle:Default:charts.html.twig', array('page' => 'charts', 'top10' => $top10Array));
}
I just want to know if I am doing this correctly and is there a better way of doing this (or right way)?
I'm also very new to Symfony and still getting my head around it. I just don't want to get into bad habits so I'm trying to do things right from the start.
I hope I explained this well enough :)
TIA
Just detected two things that are in the top of my head.
1 If you define the service like:
services:
pww.datafactorybundle.model.charts_data_model:
class: PWW\DataFactoryBundle\Model\ChartsDataModel
arguments: [ #doctrine.orm.entity_manager ]
Then you can inject it in the controller like described here, so you keep the service arguments out of the Controller:
public function chartsAction()
{
$myservice = $this->get('pww.datafactorybundle.model.charts_data_model');
$top10Array = $myservice->getChartsTop10All();
}
Secondly, I would not put this standard queries in the Model, I think is better to keep the models clean with their setters, getters and put this custom queries elsewhere like in a service that will handle all related Chart queries and you can instance from anywhere else.
I want to use the getDoctrine and getManager functions in an entity. Is this possible? or is there any way arround this? I want to insert something in a database like this :
$history = new Policy();
$history->setName($file1->getClientOriginalName());
$history->setPolicyNumber($this->getPolicyNumber());
$history->setOrderId($this->getOrderId());
$history->setPath($this->getPathFile1());
$history->setDocumentType($this->getDocument1Type());
$history->setPrintAction($this);
$em = $this->getDoctrine()->getManager();
$em->persist($history);
$em->flush();
With Doctrine ORM, Entities have an unique role : data containers!
According to Doctrine architecture, there is no reason to inject EntityManager inside.
If you need to do that, you're trying to put some code of the Business layer into layer.
So try to move your code into a service, like a manager for your Entity or if you're lazy in a controller but it's a bit crapy.
I would venture to first answer the question, and then give out advice.
If you look into source code of Doctrine2, you may to find this method in Doctrine\ORM\UnitOfWork:
/**
* #param ClassMetadata $class
*
* #return \Doctrine\Common\Persistence\ObjectManagerAware|object
*/
private function newInstance($class)
{
$entity = $class->newInstance();
if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
$entity->injectObjectManager($this->em, $class);
}
return $entity;
}
So... it means, if your entity implements \Doctrine\Common\Persistence\ObjectManagerAware you will have EntityManager inside Doctrine2 entity. That's it.
Now advice:
IT'S REALLY BAD PRACTICE, AND NOT RECOMMENDED FOR USE.
From PhpDoc of \Doctrine\Common\Persistence\ObjectManagerAware interface:
Word of Warning: This is a very powerful hook to change how you can work with your domain models.
Using this hook will break the Single Responsibility Principle inside your Domain Objects
and increase the coupling of database and objects.
I have a User entity which has an ArrayCollection of Positions. Each Position has for sure a user_id property.
Now i want to get all positions from a user (to get all i would do $user->getPositions()) that are matching a specific query, for example have a date property that matches the current date. Therefor i want to do something like $user->getCurrentPositions() and it should return a subset of the positions related to that user.
How is that possible?
EDIT:
What i really wanna do is something like this in my controller:
$em = $this->getDoctrine()->getManager();
$users = $em->getRepository('fabianbartschWhereMyNomadsAtBundle:User')->findAll();
foreach ($users as $user) {
$positions = $user->getCurrentPositions();
foreach ($positions as $position) {
echo $position->getLatitude().'<br>';
}
}
I wanna iterate over all users and from each user i want to have the relevant positions. But that isnt possible from the repository i guess, as i get the following message: Attempted to call method "getCurrentPositions" on class ...
If you are using Doctrine you can use the built-in Criteria API which is meant for this purpose exactly.
Collections have a filtering API that allows you to slice parts of data from a collection. If the collection has not been loaded from the database yet, the filtering API can work on the SQL level to make optimized access to large collections.
Ok i found out, its for sure possible with Repositories:
Entity\User.php
/**
* #ORM\Entity(repositoryClass="fabianbartsch\WhereMyNomadsAtBundle\Entity\UserRepository")
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
Entity\UserRepository.php
/**
* UserRepository
*/
class UserRepository extends EntityRepository
{
public function getCurrentPositions()
{
$query = $this->getEntityManager()
->createQuery(
"SELECT p
FROM xxx:Position p
WHERE p.start <= '2014-08-17' AND p.end >= '2014-08-17'"
);
try {
return $query->getResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
}
In the user object only related position entries are affected by the query, so is no need to join user entity with the position entity. Pretty simple, should just try out instead posting on stackoverflow, sry guys :P