Access private or protected properties from Symfony2 repository - symfony

I'm trying to access private properties of an entity from his own repository. By doing PHP tells me I can not access private or protected properties or methods.
I'm just calling a custom repository method created by me, passing an instance of the entity. When i try to get the ID for example, php throws me the error.
How I can access it?
public function customMethod($entityInstance)
{
$query = $this->getEntityManager()
->createQuery(
'SELECT c
FROM AcmeMainBundle:Content c
WHERE
c.published = 1
AND
c.id != :id
ORDER BY c.date DESC'
)
->setParameter('id',$entityInstance->id);
return $query->getResult();
}
This is an example of the custom method of my repository. Obviously is just an example, that DQL is not very usefull but when I try to access to $entityInstance->id ...
Can someone helps me?

The relationship between a Repository and an Entity is conceptual, not structural. This means that although you know they are related, PHP doesn't.
If you want to access private members of the Entity from the Repository you will have to do it like with any other class: using getters and setters.
Try this:
->setParameter('id', $entityInstance->getId());

Well, as with all normal PHP scripts, if you want to access a private or protected property you have to create a getter. DQL isn't changing anything from the PHP site, it only adds minor changes to the SQL syntax (which is just a string in PHP).
So actually, I don't see what you're trying to say with this question. That DQL should change the way PHP and OO works?

Related

How to make multiple table connections in symfony2 Repository ? Entity?

I have tables like
profile status
Profile.class
id name
1 taro
2 jiro
3 john
Status.class
id profile school date
1 1 highschool 2017-04-01
2 1 juniorhighschool 2013-04-01
3 2 highschool 2017-04-01
Status is added when status changes.
So I normally choose latest status every time I need status.
$ss = $this->em->createQuery(
"SELECT cm FROM UserBundle:Status s where c.profile = :p order by desc")
->setParameters(['p' => $profile])->getResult();
$ss[0] // Latest Status
So now I would like to put this in function.
What I want to do is getting latest status from profile.
I have a few ideas
Put this function in Profile Entity?
put this function in Profile Repository?
put this function in service???
In my opinion it should be the feature of Profile Entity, So I would like to put this in Entity though, access another from an Entity is bad manner.
Is it OK to access another entity from a Profile Repository??
Or should I use service??
You can achieve this with a method in ProfileRepository
<?php
public function getLastStatusByProfile(Profile $profile)
{
// do our query from Profile with a join on Status
}
And please, use a LIMIT 1 on your query, you only need the last result
You cannot put this in an Entity because Entities cannot be injected the Doctrine EntityManager dependency ($this->em).
To perform your "getLatestStatus()" function you need the EntityManager $this->em.
To access the EntityManager you can:
get it from the container in a Command or a Controller (e.g. in a Controller $this->get('doctrine')->getManager();)
inject it into a Service using Dependency Injection configuration files (see http://symfony.com/doc/current/service_container.html#injecting-services-config-into-a-service)
use it in a Repository because Repositories have native access to it
Usually people put functions such as getLatestStatus() in a repository, the repository becomes "the class where we put all DQL queries" and this works quite fine. This is recommanded by the official Documentation (https://symfony.com/doc/current/doctrine/repository.html) "Methods containing your query logic can then be stored in this class."
It is usual in Symfony Applications to have:
Entities having only properties, getters, setters, and some additionnal logical functions (like activate(), disable() ... functions that modify the Entity properties)
Repositories to hold DQL queries with complex logic such as getLatestStatus()
Services to hold other any other functions that read / modify data
Controllers are only gateways to use Services
So one complete example would be:
<?php
class ProfileRepository extends EntityRepository
{
/**
* #param Profile $profile
*
* #return Status
*/
public function getLatestStatus($profile)
{
$qb = $this->getEntityManager()->createQuery(
"SELECT cm FROM UserBundle:Status s where c.profile = :p order by desc")
->setParameters(['p' => $profile])
->getResult();
return $result;
}
}
And do not forget to handle the case where there is no "status" available for this profile. Do you wish to return null, raise an Exception or return a default status ?

Set up non-persistent relation in Doctrine 2

I have an object $user that has a one to many relation with $establishment. I can use:
$user->getEstablishments();
The user can select a stablishment to work on. I have this method that I call in the controller:
$user->setCurrentEstablishment($establishment);
And this one that I call in the view:
$establishment = $user->getCurrentEstablishment();
I want to be able to call:
$user->setCurrentEstablishmentBy Slug($establishment_slug);
where the slug is a string, and let the user object look for the establishment.
Doctrine discourages the practice of accessing the Entity Manager inside the Entity object, but I think that using it in the controller is even worse.
I suspect that some special Doctrine annotation exists that takes care of non persistent relations like this, or some method other than serving the Entity Manager through a service should be used here. Some easy way of referencing other entities from inside the model.
¿Is there any? ¿How could I do that?
There is no Annotation in Doctrine which could convert slug into object.
What can help You is ParamConverter, with it you can automatically convert slug from query into object. But it still must be used in Controller.
Example usage:
/**
* #Route("/some-route/{slug}")
* #ParamConverter("object", class="AppBundle:Establishment", options={"id" = "slug", "repository_method" = "findEstablishmentBySlug"})
*/
public function slugAction(Establishment $object)
{
...
Docs about param converter: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html

Add data when running Symfony migrations

I have a Symfony project that is using the DoctrineMigrations bundle, and I have a really simple question: When I run a migration (e.g., when I'm pushing an update to production), how can I insert data to the database?
For example: I have an Entity which is the type of an add. The entity is:
private $addType; // String
private $type1; // Boolean
private $type2; // Boolean
private $type3; // Boolean
I add another field ($type4), and I want to add a new record to the database, with this values:
$addType = 'Type number 4';
$type1 = false;
$type2 = false;
$type3 = false;
$type4 = true;
How can this be done with DoctrineMigrations? Is it possible?
Using the Entity Manager as suggested in another answer is not a good idea, as it leads to troubles later.
In the first migration, I created a table with users and populated some users via $em->persist($user); which seemed fine at the beginning.
But after a month, I added a phone column to my User model. And Doctrine generates INSERT statements with this column within the first migration, which fails due to the non-existing column phone. Of course it doesn't exist yet in the first migration. So it is better to go with pure SQL INSERTs.
I just asked a related related question.
It is possible to use the migrations bundle to add data to the database. If you add a new property and use the doctrine mapping then the
php app/console doctrine:migrations:diff
command will generate a new migration file. You can just put your insert statements inside this file using the syntax:
$this->addSql('INSERT INTO your_table (name) VALUES ("foo")');
Make sure you put it after the auto-generated schema changes though. If you want to separate your schema changes and your data changes then you can use
php app/console doctrine:migrations:generate
to create an empty migrations file to put your insert statements in.
Like I said in my related question, this is one way to do it, but it requires manually creating these if you want to change this data in the database.
Edit:
Since this answer seems to get a few views I think it's worth adding that to more clearly separate the data changes from the schema changes there is a postUp method that can be overridden and that will be called after the up method.
https://www.doctrine-project.org/projects/doctrine-migrations/en/3.0/reference/migration-classes.html#postup
I've "found" the correct way to solve my problem (insert data after running migrations, using my entity classes).
Here is: https://stackoverflow.com/a/25960400
The idea is to declare the migration as ContainerAware, and then, from the postUp function, call the DI to get the EntityManager. It's really easy, and you can use all your entities and repositories.
// ...
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class Version20130326212938 extends AbstractMigration implements ContainerAwareInterface
{
private $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function up(Schema $schema)
{
// ... migration content
}
public function postUp(Schema $schema)
{
$em = $this->container->get('doctrine.orm.entity_manager');
// ... update the entities
}
}
when you make the new field you need to enter this annotation "options={"default":1}" and it should work.
/**
* #var boolean
* #ORM\Column(name="type4", type="boolean", options={"default":1})
*/
private $type4 = true;
Took me some time to figure this out :)
It does, if you know how to format the array;
$this->connection->insert('user', ['id' => 1, 'gender' => 'Male']);
this is good solution for me. Just use bin/console make:migration and when migration is generated just edit if and add "DEFAULT TRUE":
$this->addSql('ALTER TABLE event ADD active TINYINT(1) NOT NULL DEFAULT TRUE');
It doesn't sound a good idea to fill date in migration, not its responsibility, symfony has a way of doing that. https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html

$em->remove() symfony2 erasing all rows

I have an issue when erasing something from a BD.
The problem is that it not only erase the object i looked for (using findOneBy), but all the objects related to the principal id.
//---Controller
$new = $this->getDoctrine()->getManager();
$OBJcar = $new->getRepository('SomeOtherBundle:CarEntityClass')
->findOneBy(array('idOwner' => $idowner, 'idCar' => $idcar));
if($OBJcar){
$new->remove($OBJcar);
$new->flush();
$msj="The car for an specific owner has been erased.";
}
//---Profiler (Query)
"START TRANSACTION"
Parameters: { }
Time: 0.22 ms
DELETE FROM schema.CarTable WHERE id_owner = ?
Parameters: ['123456']
Time: 0.63 ms
"COMMIT"
Parameters: { }
Time: 0.63 ms
How to erase the one row i am getting from the db?
I voted down the answer above, because I'm tired of people using string DQLs.
It's not standartized, non-object oriented(even though in background dql operates with objects), it doesn't use caching mechanisms query builder provides, it's non-flexible and simply looks unclean.
Here is the "right way"(IMHO):
You add the repository class for entity
You add the method you need with a query builder in it
You call the method while passing parameters needed for specific REPOSITORY OBJECT ORIENTED ACTION
You get an easy-to-handle result
Here's the code:
namespace ProjectName\BundleName\Repository;
use Doctrine\ORM\EntityRepository;
class CarRepository extends EntityRepository
{
public function deleteCarWithOwner($ownerId,$carId)
{
$isDeleted = $this->createQueryBuilder("car")
->delete()
->where('car.id = :carId')->setParameter("carId", $carId)
->andWhere('car.idOwner = :ownerId')->setParameter("ownerId", $ownerId)
->getQuery()->execute();
return $isDeleted;
}
}
Also, refer to http://doctrine-orm.readthedocs.org/en/latest/reference/query-builder.html for query builder details. There are a lot of "pros" and I see no "cons" for using builder.
LATE UPDATE
Also, a lot of Doctrine's entity events are not dispatched when using DQL.
Use DQL
$query = $em->createQuery('DELETE SomeOtherBundle:CarEntityClass c WHERE c.idOwner = 4 AND c.id = 10');
$query->execute();
This will remove only single car with ID 10 and owner with ID 4.

Symfony/Doctrine: fetching data as object , still get array

I have in my controller $id it's a foreign key
$query = $em->getRepository('SurgeryPatientBundle:Patients')->findPatientByUserID($id);
And in my repository file this function
public function findPatientByUserID($id)
{
return $this->getEntityManager()
->createQuery('SELECT p FROM SurgeryPatientBundle:Patients p WHERE p.user ='.$id.'')
->execute();
}
I want get an instance of object but still get an array. Query with find($id) works good
edit
Problem solves , I'm so stupid , I had invoked to $query[0]
You can use $query->getSingleResult(); as well
see here
http://docs.doctrine-project.org/en/2.1/reference/dql-doctrine-query-language.html#query-result-formats
If you want to grab the object, you shouldn't be using DQL. Doctrine entities have a find function that takes care of this for you.
Instead of all that code, you can just use (in your controller):
$em->getRepository('SurgeryPatientBundle:Patients')->find($id);
DQL is very powerful, but for simple lookups like this using the built in find methods will be more efficient & provide the entities as doctrine objects.

Resources