Array as parameter in Doctrine SQLFilter - symfony

I'm trying to create Doctrine SQLFilter. I need to filter for "deleted" field. But i want to make filter works with both (true and false) values too.
Something like this:
<?php
namespace Rem\CostsBundle\Doctrine;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
class ItemDeletedFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if ($targetEntity->getReflectionClass()->name != 'Rem\CostsBundle\Entity\Item') {
return '';
}
$fdata = $this->getParameter('deleted');
$filter = '1<>1';
foreach ($fdata as $param) {
$filter .= sprintf('OR %s.deleted = %s', $targetTableAlias, $param);
}
return $filter;
}
}
But when I'm trying to set array of posible filter values in controller
$filters
->enable('costs_item_deleted')
->setParameter('deleted', [true, false]);
I get an error 500
Warning: PDO::quote() expects parameter 1 to be string, array given
This is clear situation. But, after all HOW to send array of params to my SQL filter?
UPD after Dmitry answer: This is not actualy what I wanted. Let say: what if I wanted to filter by few values of field? For records of 2015 and 2016 years for example... So i need to set some sort of array-of-years in ->setParameter. But it want only strings! And sends an error when i'm trying to set something else.
How do you solve this?
Or even more complicated example. What if I need to filter by relational field. In this case I need to set entity as param of filter. Or even ArrayCollection of entities!
For now I'm decide it like this: I json_encode array before set it to setParameter in controller. And then I json_decode it in Filter class. BUT! There in Filter class I need to make one more step. I need to remove single and doublequotes from json string. Because they was added by setParameter to escape string (thats why we love it )) ).
Code hacks like this we call "crutches" here in Russia. So I'd like to avoid of them and write more elegant code )

Make it IN instead of comparing.
foreach ($fdata as $param) {
$filter .= sprintf('OR %s.deleted IN (%s)', $targetTableAlias, param);
}

Related

can't get result object in doctrine

I got DQL like this:
$sql =$qb->select('login,id')
->from('Cusomter','c')
->where('c.login = :login')
->setParameter('login',$login);
$rs = $sql->getQuery()->getResult();
I want the result would return as object with 2 properties: Login,id.
Is there anyway i can do that? I tried CustomHydrator but it won't work out.
this is my CustomHydrator:
namespace Hydrator;
use Doctrine\ORM\Internal\Hydration\AbstractHydrator;
class CustomHydrator extends AbstractHydrator
{
protected function hydrateAllData()
{
return $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
}
}
Then i added $em->getConfiguration()->addCustomHydrationMode('CustomHydrator', 'Hydrator\CustomHydrator');
and run :$rs = $sql->getQuery()->getResult('CustomHydrator');
There are a few ways to go about this. If this is a query that you need to reuse, then Doctrine custom hydrators is perhaps the best approach. If not, you could simply cast the result to an object.
If you need a single result:
$sql =$qb->select('c.login, c.id')
->from('Cusomter','c')
->where('c.login = :login')
->setParameter('login', $login);
$rs = (object) $sql->getQuery()->getSingleResult();
If you need to return an array of objects (i.e. multiple result items), then you could just cast each result:
$sql =$qb->select('c.login, c.id')
->from('Cusomter','c')
->where('c.login = :login')
->setParameter('login', $login);
$rs_new = array_map(function ($value) {
return (object) $value;
}, $sql->getQuery()->getResult());
Better way to use Entity as result http://symfony.com/doc/current/doctrine.html#creating-an-entity-class
As I understand, you need just one result, so use next code inside repository:
$rs = $this->findOneBy(['login' => $login]);

Symfony2 dynamic queryBuilder

I'm making a tool in which a user can view data from an entity, where they can choose what data and how they see the records.
I created a form with two date fields (start and end) and a list of fields that correspond to data counts and sums of the entity.
My question is:
How I can create a dynamically QueryBuilder that allows me to add fields based on what the user wants to see?
EDIT for Symfony2 dynamic queryBuilder
public function reportData($fields, $dateStart, $dateFinish)
{
$em = $this->getEntityManager()
->getRepository('AcmeBundle:Entity');
$query = $em->createQueryBuilder('e');
foreach($fields as $field)
{
switch($field)
{
case 'totalResults':
$query->setect('SUM(e.id) AS '.$field);
break;
}
}
$query->addWhere('e.dateStart >= :dateStart');
$query->addWhere('e.dateFinish <= :dateFinish');
...
Something like this ? You store all your select queries in an array, then pass the array to the query builder after testing each of your fields.
public function reportData($fields, $dateStart, $dateFinish)
{
$em = $this->getEntityManager()
->getRepository('AcmeBundle:Entity');
$query = $em->createQueryBuilder('e');
$select_array = array();
foreach($fields as $field)
{
switch($field)
{
case 'totalResults':
$select_array[] = 'SUM(e.id) AS '.$field;
break;
}
}
$query->select($select_array);
$query->addWhere('e.dateStart >= :dateStart');
$query->addWhere('e.dateFinish <= :dateFinish');
....
Basically, you want to keep on adding the
Select Fields
based upon the conditions.
So, the solution is simple.
You can use,
$queryBuilder->addSelect();
See Doctrine Query Builder Documentation
I would do a regular full query then filter it into a not doctrine object (dao/dto) then display it.
This way you can do the complex and optimized query first, then filter the result on whatever you want, even if it's not related to the query itself

Laravel Eloquent - Encrypting/Decrypt Data on call

I can use Crypt to encrypt/decrypt my data. I want to encrypt some information in my db (such as the name, email, phone number to name a few).
Assuming that I want EVERYTHING to be encrypted, I want to be able to do this in the background by itself, which I can perform by overwriting the create and save functions:
// For instance, the save() function could become
public function save(array $options = array())
{
foreach ($this->attributes as $key => $value)
{
if (isset($value)) $this->attributes[$key] = Crypt::encrypt($value);
}
return parent::save($options);
}
Now, I want the decryption to be performed the same way, so that when I say User::find($id), the returned $user is already decrypted. Also other functions such as firstOrFail() get() first() and all to work as well.
I also would like this functionality to be extended when I use relationships (so User::with('someOtherTable')->find($id) also work).
Would this be possible? If this is not possible, I am thinking of creating a helper function decyrpt()
function decrypt($array)
{
if (!is_array($array)) return Crypt::decrypt($array);
$result = [];
foreach($array as $key => $value) $result[$key] = decrypt($value);
return $result;
}
And pass all my results through this first, and then start using them, but it would be nicer if Laravel would provide this, or if there was a "Laravel Way" of doing this.
It doesn't really make sense to encrypt everything. For example, you never want to encrypt the primary key; that doesn't even make sense. Likewise you probably don't want to encrypt the date fields; you'll lose the ability to perform any sort of SQL query on them.
With that in mind, you can try something like this:
class BaseModel extends Eloquent {
protected $encrypt = [];
public function setAttribute($key, $value)
{
if (in_array($key, $this->encrypt))
{
$value = Crypt::encrypt($value);
}
return parent::setAttribute($key, $value);
}
public function getAttribute($key)
{
if (in_array($key, $this->encrypt))
{
return Crypt::decrypt($this->attributes[$key]);
}
return parent::getAttribute($key);
}
public function attributesToArray()
{
$attributes = parent::attributesToArray();
foreach ($attributes as $key => $value)
{
if (in_array($key, $this->encrypt))
{
$attributes[$key] = Crypt::decrypt($value);
}
}
return $attributes;
}
}
then have all you models extend this one, and set the $encrypt property to whatever columns you want encrypted for that particular model.
P.S. If you want to use Eloquent's accessor functionality, you'll have to play with this a bit more.
It's worth mentioning Elocrypt library for Laravel 4. It's a more elaborate solution that works the same way. If you're using Laravel 5 use this one instead: Elocrypt 5.

How to display LONGBLOB field data in TWIG file

I am working in Symfony2. I have a field in table that uses LONGBLOB. So now I want to display field data.When I display this LONGBLOB field data it show's me text like this
Resource id #404 but actually in field I have stored the dummy text.
This is my defaultcontrolle file code
DefaultController.php
function video_showAction($id)
{
$em = $this->getDoctrine()->getManager();
$video = $em->getRepository('MyBundle:Video')->find($id);
return $this->render('MyBundle:Default:VideoShow.html.twig', array('qShow' => $video ));
}
VideoShow.html.twig
Description: {{ qShow.description }}
// it will display "Resource id #404"
How can I display the actual data not he Refrence.
As you mentioned in your own answer, you should rewrite your getter function.
I propose something like that:
private $descriptionAsText = null;
public function getDescription()
{
if (is_null($this->descriptionAsText))
{
$this->descriptionAsText = stream_get_contents($this->description);
}
return $this->descriptionAsText;
}
If your stream can change in the same instance of the entity, you can eventually use:
public function getDescription()
{
rewind($this->description);
return stream_get_contents($this->description);
}
I dislike your current method, as if you need to use {{ qShow.description }} twice or more, you will get troubles because of your stream offset.
You need to rewind your resource each time you're doing a stream_get_contents, because it places the offset at the end of your stream (or at the specified length).
You can reproduce this behavior using the following code:
<?php
file_put_contents("/tmp/test.txt", "Hello, world!");
$handle = fopen("/tmp/test.txt", "r");
$contents = stream_get_contents($handle);
echo "Contents A = {$contents}\n"; // Contents A = Hello, world!
$contents = stream_get_contents($handle);
echo "Contents B = {$contents}\n"; // Contents B =
fclose($handle);
This behaviour is there since PHP 5.3 (I guess), so if you try this code on Codepad (who uses 5.2.5), you will not reproduce.
I have found the Solution.
I just need to use stream_get_contents. So I have put this into Getter method of my Entity.
Like this
MyEntity.php
// Getter Function need to be change like this
public function getDescription()
{
if ($this->description != '')
return stream_get_contents($this->description);
return $this->description;
}
Now When I display the content it shows me the Actual Data that the Recourse Id contains.
For using LONGBLOB firs you need do the following works. this works are describe in detail at the following link please check that you do all of them and then try again to show your qShow.description on your twig file. I think "Resource id #404" error say you that you have problem reading your resource with 404 ID Number.
http://symfony2.ylly.fr/add-new-data-type-in-doctrine-2-in-symfony-2-jordscream/

How do I filter results in APYDataGridBundle in Symfony2?

I have been looking for hours for a way of setting a condition on the list of items that an APYDataGridBundle grid should return but could not find an answer.
Is there a way to set a DQL Query and pass it to the grid to display the exact query results I want to fetch?
This is the code:
public function filteredlistAction(){
// Create simple grid based on the entity
$source = new Entity('ACMEBundle:MyEntity');
// Get a grid instance
$grid = $this->get('grid');
// Attach the source to the grid
$grid->setSource($source);
...
...
**$grid->getColumns()->getColumnById('myentity_filter_column')->setData('the exact value I tried to match');**
// Manage the grid redirection, exports and the response of the controller
return $grid->getGridResponse('ACMEBundle:MyEntity:index_filteredlist.html.twig');
}
You can add a callback (closure or callable) to run before QueryBuilder execution - its done like this :
$source->manipulateQuery(
function ($query)
{
$query->resetDQLPart('orderBy');
}
);
$grid->setSource($source);
$query is an instance of QueryBuilder so you can change whatever you need to
Example taken from docs here
A more "complicated" query.
$estaActivo = 'ACTIVO';
$tableAlias = $source->getTableAlias();
$source->manipulateQuery(
function ($query) use ($tableAlias, $estaActivo)
{
$query->andWhere($tableAlias . '.estado = :estaActivo')
->andWhere($tableAlias . '.tipoUsuario IN (:rol)')
->setParameter('estaActivo', $estaActivo)
->setParameter('rol', array('VENDEDOR','SUPERVISOR'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
}
);
Cheers!

Resources