Database Searching Using Doctrine and Symfony2 - symfony

So I'm currently trying to perform a simple search using Symfony2 and Doctrine. Something similar to this: http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/searching.html
I've currently got the following YAML file setup to generate my entities. It generates my class Style entity correctly as a class.
...\Style:
type: entity
table: styles
id:
id:
type: integer
generator:
strategy: IDENTITY
actAs:
Searchable:
fields: [title]
batchUpdates: true
fields:
title:
type: string
length: 150
unique: true
In my controller, I'm trying to run a search on that table based on a string.
public function searchAction($pattern)
{
$repository = $this->getDoctrine()->getRepository('..:Style');
$search = $repository->search($pattern);
return $this->outputize($search);
}
However, when I try executing the code, I get the following exception.
Undefined method 'search'. The method name must start with either findBy or findOneBy!
Am I generating my entities correctly or is there something I'm clearly missing?
On a side note, when I look at my Entity/Style.php after generating, there is no clear method ->search(), is the function supposed to be generated by Symfony here?

search() is not a function supported in Symfony2. You're looking at the Symfony 1.x documentation, and Symfony2 is really different from Symfony 1.x so for reference, you should always use the doc.
There are several ways to fetch entities in Symfony2. Here are a few examples:
Find
$user = $this->getDoctrine()
->getRepository('UserBundle:User')
->find($user_id)
;
DQL:
$query = $em->createQuery(
'SELECT b FROM YourBundle:Bid b WHERE b.property_id = :property_id ORDER BY b.amount DESC'
)->setParameter('property_id', $property_id);
try {
$bids = $query->getResult();
} catch (\Doctrine\Orm\NoResultException $e) {
//Handle No Result Exception here
}
Refer to the Doctrine guide for Symfony2 here: http://symfony.com/doc/current/book/doctrine.html

Hello you can do it in symfony 3
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT p
FROM AppBundle:Hotel p
WHERE p.address like :location
ORDER BY p.address ASC'
)->setParameter('location','%'.$request->get('location').'%' );
$hotel = $query->getResult();

Related

How to add custom property to Symfony Doctrine YAML mapping file

Can anyone tell me how to add custom property to doctrine ORM yml file?
My idea is to add a property like this:
fields:
name:
type: string
localizable: true
Then I would like to get information about this localizable property by using
protected function getEntityMetadata($entity)
{
$factory = new DisconnectedMetadataFactory($this->getContainer()->get('doctrine'));
return $factory->getClassMetadata($entity)->getMetadata();
}
and then:
$met = $this->getEntityMetadata($bundle.'\\Entity\\'.$entity);
$this->metadata = $met[0];
$fields = $this->metadata->fieldMappings;
if (isset($fields)) {
foreach ($fields as $field => $fieldMapping) {
if (isset($fieldMapping['localizable']) && $fieldMapping['localizable'] == true) {
// Do sth with it
}
}
}
The way doctrine is written makes this awkward. It seems like you'd like to keep the Yaml mapping but just add a single property. I think you can create your own custom driver extending from the one provided. The Yaml driver has mostly private methods so overriding a little bit of the functionality is difficult, but it is possible.
I created a custom driver that extends from the SimplifiedYamlDriver. The naming of the driver is important because doctrine extension will try to load one of their drivers based what comes before Driver. It also does a strpos check for Simplified in the name, so I think the safest bet is to keep the original name completely and give the original an alias.
use Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver as BaseDriver;
class SimplifiedYamlDriver extends BaseDriver
{
public function loadMetadataForClass($className, ClassMetadata $metadata)
{
parent::loadMetadataForClass($className, $metadata);
$element = $this->getElement($className);
if (!isset($element['fields'])) {
return;
}
foreach ($element['fields'] as $name => $fieldMapping) {
if (isset($fieldMapping['localizable'])) {
$original = $metadata->getFieldMapping($name);
$additional = ['localizable' => $fieldMapping['localizable']];
$newMapping = array_merge($original, $additional);
$metadata->fieldMappings[$newMapping['fieldName']] = $newMapping;
}
}
}
}
Then I told Symfony to use this driver by overriding the class inside app/config/parameters.yml
parameters:
doctrine.orm.metadata.yml.class: MyBundle\SimplifiedYamlDriver
Then I updated the mapping like in your example inside MyBundle/Resources/config/doctrine/Foo.orm.yml
MyBundle\Entity\Foo:
type: entity
id:
id:
type: integer
generator:
strategy: IDENTITY
fields:
text:
type: string
localizable: true
And I can fetch this mapping wherever I have access to doctrine with:
$mapping = $this
->getDoctrine()
->getEntityManager()
->getClassMetadata(Foo::class)
->getFieldMapping('text');
Will give me:
Array
(
[fieldName] => text
[type] => string
[columnName] => text
[localizable] => 1
)
Unfortunately, this is impossible without rewriting a significant part of Doctrine DBAL. This would impact drivers (YAML, annotation...), meta data generator...
In your case, the simplest I see would be to add a custom type let's say LocalizableString (I guess at most you will need that and maybe LocalizableText).
Adding a type is relatively straightforward, since you can extend a base type so you don't have to write any SQL. You can refer to Doctrine documentation here and Doctrine bundle one here.
Then you can just do:
$met = $this->getEntityMetadata($bundle.'\\Entity\\'.$entity);
$this->metadata = $met[0];
$fields = $this->metadata->fieldMappings;
if (isset($fields)) {
foreach ($fields as $field => $fieldMapping) {
if ($this->getClassMetadata()->getTypeOfField($field) === 'localized_string') {
// Do sth with it
}
}
}

Symfony2 Doctrine2 native queries basics

I am developing a basic web-app in my job. I have to work with some sql server views. I made the decision of trying native queries, and once tested it's functionality, try to write some classes to code all the queries and kinda forget their implementation.
So my issue is, I've got an Entity in Acme/MyBundle/Entity/View1.php.
This entity has got all the attributes matching the table and also it's getters and setters.
I guess this entity is well mapped to the DB (Doctrine cant work with views easily).
My aim is to let a Controller be able to fetch some data from those views(SQL SERVER) and return it to the view (twig) so it can display the info.
$returned_atts = array(
"att1" => $result[0]->getAttribute1(), //getter from the entity
"att2" => $result[1]->getAttribute2(), //getter from the entity
);
return $returned_atts;`$sql = "SELECT [Attribute1],[Attribute2],[Attribute3] FROM [TEST].[dbo].[TEST_VIEW1]"; //THIS IS THE SQL SERVER QUERY
$rsm = new ResultSetMapping($em); //result set mappin object
$rsm->addEntityResult('Acme\MyBundle\Entity\View1', 'view1'); //entity which is based on
$rsm->addFieldResult('view1', 'Attribute1', 'attribute1'); //only choose these 3 attributes among the whole available
$rsm->addFieldResult('view1', 'Attribute2', 'attribute2');
$rsm->addFieldResult('view1', 'Attribute3', 'attribute3');
//rsm built
$query = $em->createNativeQuery($sql, $rsm); //execute the query
$result = $query->getResult(); //get the array
It should be possible to return the array straight from the getResult() method isn't it?
And what's killing me, how can I access the attribute1, attriute2 and attriute2?
$returned_atts = array(
"att1" => $result[0]->getAttribute1(), //getter from the entity
"att2" => $result[1]->getAttribute2(), //getter from the entity
);
return $returned_atts;`
If you want result as array, you don't need to use ResultSetMapping.
$sql = " SELECT * FROM some_table";
$stmt = $this->getDoctrine()->getEntityManager()->getConnection()->prepare($sql);
$stmt->execute();
$result = $stmt->fetchAll();
That is a basic example for controller action. You can dump the result, use var_dump(), to see how to access your particular field values.
More examples here Doctrine raw sql

Elastic Search : How to get most researched terms

i m implemeting elasticsearch in a symfony2 project with fos_elastica.
everythings works fine ( indexing data, updating, etc.)
i m currently looking for user behavior analysis : i would like to get the 10 most user searches or keywords in order to re-query it .
for example :
if 45% of searches are about yellow balloons and 45% are about red balloons, i would like to suggest on my homepage some yellow or red balloons
firstly, i was thinking about creating a symfony2 entity to save user search with a timestamp then compute last 1000 searches to get the most famous keywords. although it would surely work , that would be resource killer.
i was wondering if elasticsearch is able to provide these and how to implement it .
i ve read that i could create an index to store my user queries ( and that would be awsome, cause i could use facets to compute them really easily ) , but i don t know how to do save it directly in elastic search from symfony2 without an dedicated entity.
Okay, i finally got it !
here are the different steps :
1) create a new index in config.yml with a specific mapping for your keywords search
in config.yml
indexes:
your_index:
types:
search:
mappings:
value: {type:string}
date : {type:date}
provider: acme\AppBundle\Service\SearchProvider
2) create a new class SearchProvider in Service directory
in acme\Appbundle\Service\SearchProvider
<?php
namespace acme\AppBundle\Service;
use FOS\ElasticaBundle\Provider\ProviderInterface;
use Elastica\Type;
use Elastica\Document;
class SearchProvider implements ProviderInterface
{
protected $searchType;
private $search;
public function __construct(Type $searchType)
{
$this->searchType = $searchType;
}
// the function you will call from your service
public function add( $search )
{
$this->search = $search;
$this->populate();
}
/**
* Insert the repository objects in the type index
*
* #param \Closure $loggerClosure
* #param array $options
*/
public function populate(\Closure $loggerClosure = null, array $options = array())
{
if ($loggerClosure) {
$loggerClosure('Indexing users');
}
$date = time();
$document = new Document();
$document->setData(array('value' => $this->search, 'date' => $date ) );
$this->userType->addDocuments(array($document));
$this->userType->getIndex()->refresh();
}
}
3) create a new service declaration in your service.yml
services:
acme.search_provider:
class: acme\AppBundle\Service\SearchProvider
arguments:
- #fos_elastica.index.recetas.search
tags:
- { name: fos_elastica.provider, index: your_index, type: search }
4) call your service to store new searches like this
$this->get("acme.search_provider")->add("kapoue");
kapoue will be added to the searches.
5) get all the search keywords and rank it with aggregation
$es = $this->get('fos_elastica.index.acme.search');
$query = new \Elastica\Query();
$aggregation = new \Elastica\Aggregation\Terms("top_hits");
$aggregation->setField('value');
$aggregation->setSize( 3 );
$query->addAggregation($aggregation);
$result = $es->search($query);
$mostResearched = $result->getAggregation("top_hits");
print_r ( $mostResearched ); die();

Symfony2 Doctrine Join Entity

I have a entity with the next join:
class blogComment
{
....
/**
* #ORM\OneToMany(targetEntity="BlogComment", mappedBy="replyTo")
*/
protected $replies;
....
}
Now I get successfully all the replies. But I only want to get: where active = true
How to do that?
Oke if you guys recommend to get the comments by query in the controller how to build a nested array to get result like this:
For solving the part where you only want active replies there are a couple of options:
1) Use some custom DQL in a repository:
$dql = 'SELECT bc FROM BlogComment bc WHERE bc.replyTo = :id AND bc.active = :active';
$q = $em->createQuery($dql)
->setParameters(array('id' => $id, 'active' => true));
2) Using ArrayCollection::filter() in the getter:
public function getReplies()
{
return $this->replies
->filter(function ($reply) {
return $reply->isActive();
})
->toArray();
}
3) Using ArrayCollection::matching() (Collection Criteria API) in the getter:
use Doctrine\Common\Collections\Criteria;
// ...
public function getReplies()
{
$criteria = new Criteria::create()
->where(Criteria::expr()->eq('active', true));
return $this->replies
->matching($criteria)
->toArray();
}
4) Use Filters. These can add where clauses to queries regardless of where that query is generated. Please see the docs.
If you want to be able to fetch an entire set of replies, nested and all, in a single query, you need to implement some kind of "tree" of "nested set" functionality. I'd advise you to look at the Tree behavior of l3pp4rd/DoctrineExtensions.
http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
Wherever you are obtaining your blog comments to display them (probably on a controller), you need to customise your query so that only the active replies are extracted. Something like:
$query = $em->createQuery('SELECT b FROM blogComment b JOIN b.replies r WHERE r.active = :active');
$query->setParameter('active', true);
$blogComments = $query->getResult();
EDIT:
For your new requirement of nested replies, you would need to specify a relationship between a comment entity and its parent comment.

Symfony2 (doctrine2) native sql insert

How to insert data in symfony2 doctrine2 on native sql?
My query
insert into propriedades (id,name,descripcion,num_lote,cod_imovel,imovel,convenio,proprietar,cpf,area_ha,perimetro,location,centro) VALUES (nextval('propriedades_id_seq'),'?','?','?','?','?','?','?','?','?','?',ST_GeomFromKML('<Polygon><outerBoundaryIs><LinearRing><coordinates>".$terra['coordinates']."</coordinates></LinearRing></outerBoundaryIs></Polygon>'),ST_Centroid(ST_GeomFromKML('<Polygon><outerBoundaryIs><LinearRing><coordinates>".$terra['coordinates']."</coordinates></LinearRing></outerBoundaryIs></Polygon>')))
You have to use $conn->insert('table', $dataArray);. See documentation
In 2020 you can do something like (example query, adapt it to your params):
$query = "
INSERT INTO `user_challenges_claimed`
SET
`season_id` = :seasonId,
`user_id` = :userId,
`interval_type` = :intervalType,
`is_claimed` = true
ON DUPLICATE KEY UPDATE
`is_claimed` = true
;
";
// set query params
$queryParams = [
'seasonId' => $seasonId,
'userId' => $userId,
'intervalType' => $intervalType,
];
// execure query and get result
$result = $this->manager->getConnection()
->executeQuery(
$query,
$queryParams
);
// clear manager entities
$this->manager->clear();
// optional - assert row has been inserted/modified
if ($result->rowCount() < 1) {
throw new ChallengeAlreadyClaimedException(
"[{$seasonId}:{$userId} - {$intervalType}]"
);
}
$this->manager is an object implementing EntityManagerInterface (ie EntityManager).
Usually you do not use what you call native sql in a symfony 2 project but the high level Doctrine ORM layer.
However there is a doctrine dbal layer which enables e.g. mysql queries instead of DQL. Here is the reference
http://symfony.com/doc/2.0/cookbook/doctrine/dbal.html

Resources