I am developing a project using Symfony 5. One of my use-cases involves reading a collection from the Database, with the items sorted by creation order, descending (newest first). I am using the "Timestampable" extension from "stof/doctrine-extensions-bundle" to save the createdAt and updatedAt timestamps in my entity.
According to Doctrine documentation, I can sort items ussing the Repository methods:
$sortedEntities = $repository->findBy(array('createdAt' => 'DESC'));
This is the attribute in question:
/**
* #var \DateTime $createdAt
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
*/
private $createdAt;
However, using 'ASC' or 'DESC' seems to have no impact on the ordering of the list.
You are not reading the documentation correctly. The orderBy is the second argument, not the first.
The example given in the docs is
$tenUsers = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0);
Here, you can see the orderBy (name, ASC) is the second arg. The first arg is a where arg - in this case, WHERE age = 20.
Here is the full signature from Doctrine\Persistence\ObjectRepository
/**
* Finds objects by a set of criteria.
*
* Optionally sorting and limiting details can be passed. An implementation may throw
* an UnexpectedValueException if certain values of the sorting or limiting details are
* not supported.
*
* #param array<string, mixed> $criteria
* #param string[]|null $orderBy
* #param int|null $limit
* #param int|null $offset
* #psalm-param array<string, 'asc'|'desc'|'ASC'|'DESC'> $orderBy
*
* #return object[] The objects.
* #psalm-return T[]
*
* #throws UnexpectedValueException
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null);
I hope that clarifies it for you. :-)
[EDIT] In response to your comment, you cannot use true as a value for the first argument. Look at the signature I posted. The first argument is #param array<string, mixed>, so it needs an array. Try this then:
sortedEntities = $repository->findBy(array(), array('createdAt' => 'DESC'));
Related
I am beginner in Laravel.
I use in my project Laravel 7.
I have cache system in my project.
I have cache in my project with keys:
category
category.id
category.subcategory.id
product.all
etc.
I need function to remove cache.
I write this:
private function deleteCache(string $keyToRemove)
{
Cache::forget($keyToRemove);
}
Is universal cache removal possible?
I need a function that will be
Remove selected keys:
deleteCache(['category.100', 'product.all', 'category.1'])
Remove all cache with category (for example: category.1, category.all, category, category.tree, category.subcategory.1 etc).
deleteCache(['category.*'])
How can I make it?
TL:DR What you need is not available by default, you need customized, wrapper methods that requires "technical" knowledge about the cache driver(underlying technology) you choose.
Laravel cache supports multiple technologies(drivers) including redis, database, file, memcached etc. All these drivers implement the same interface.
namespace Illuminate\Contracts\Cache;
interface Store
{
/**
* Retrieve an item from the cache by key.
*
* #param string|array $key
* #return mixed
*/
public function get($key);
/**
* Retrieve multiple items from the cache by key.
*
* Items not found in the cache will have a null value.
*
* #param array $keys
* #return array
*/
public function many(array $keys);
/**
* Store an item in the cache for a given number of minutes.
*
* #param string $key
* #param mixed $value
* #param float|int $minutes
* #return void
*/
public function put($key, $value, $minutes);
/**
* Store multiple items in the cache for a given number of minutes.
*
* #param array $values
* #param float|int $minutes
* #return void
*/
public function putMany(array $values, $minutes);
/**
* Increment the value of an item in the cache.
*
* #param string $key
* #param mixed $value
* #return int|bool
*/
public function increment($key, $value = 1);
/**
* Decrement the value of an item in the cache.
*
* #param string $key
* #param mixed $value
* #return int|bool
*/
public function decrement($key, $value = 1);
/**
* Store an item in the cache indefinitely.
*
* #param string $key
* #param mixed $value
* #return void
*/
public function forever($key, $value);
/**
* Remove an item from the cache.
*
* #param string $key
* #return bool
*/
public function forget($key);
/**
* Remove all items from the cache.
*
* #return bool
*/
public function flush();
/**
* Get the cache key prefix.
*
* #return string
*/
public function getPrefix();
}
Depending on the driver you choose - you need customized methods to achieve what you need.
For your first question, the following method would be useful to remove multiple keys.
public function deleteCache(array $keys)
{
foreach ($keys as $key) {
Cache::forget($key);
}
}
I am familiar with redis so i will give examples around it. If you are going to use redis as cache driver - it is better to modify that method like this; Since redis's delete command supports deleting multiple keys at once. This one is more effective than the previous one.
public function deleteCache(array $keys)
{
Redis::del($keys);
}
One trick is to be careful about cache prefix. If you are using cache prefix(defined in your cache config file) - then you need to prepend those prefix to keys.
For your second question(Remove all cache with category) there are several ways to do it but some of them wouldn't be performance/production friendly. In redis you may execute some command such as keys or scan to iterate through database and then invoke the previously defined method with the returned results.
Especially keys command should only be used in production environments with extreme care.
redis key
redis scan
Redis is only example - if you are going to use database cache driver - then you need to implement methods to satisfy your case. It will require technical knowledge about the both how laravel implements it via database(tables, queries etc) and how your extended methods will use it(tables, queries, columns, indexes etc)
Is there a way with php-cs-fixer to define an order for Symfony annotations/PHPDoc ?
Here is two examples of a controller method and an entity property :
/**
* #Security()
*
* #ParamConverter()
*
* #Rest\Post()
* #Rest\View()
*
* #param Request $request
* #param xxxInterface $item
*
* #return \FOS\RestBundle\View\View
*/
public function myAction(Request $request, xxxInterface $item)
and
/**
* #var itemInterface
* #ORM\ManyToOne()
* #ORM\JoinColumn()
* #JMS\Groups()
* #JMS\AccessType()
* #MyCustomAssert\Assert1
*/
protected $item;
For methods, I want to set the order to #Security, #ParamConverter, #Rest then PHPDoc and for properties, I always want #MyCustomAssert at the end.
Is this something possible with php-cs-fixer ?
I think it isn't possible with php-cs-fixer (https://mlocati.github.io/php-cs-fixer-configurator/?version=2.9#version:2.15 might help searching)
However the slevomat coding standard for php code sniffer includes a sniff "SlevomatCodingStandard.Commenting.DocCommentSpacing" which allows you to configure annotationsGroups
The phpcbf script can reorder your annotations using this snif.
I'm trying to sort some data using KnpPaginator, but there is no example of how to use "options" array.
For now I sort data, by passing it to the findBy itself, but I can't sort by subobject attribute this way.
$this->limit = $request->query->getInt('limit', $container->getParameter('pagination.default_limit'));
$this->page = $request->query->getInt('page', 1);
$sortColumn = $this->camelcase($request->query->get('sort', 'id'));
$sortDirection = $request->query->get('direction', 'asc');
$this->sort = array($sortColumn => $sortDirection);
$this->list = $this->getRepo(Service::class)->findBy(
array('user' => $this->getUser()->getId()),
$this->sort
);
$pagination = $this->get('knp_paginator')->paginate(
$this->list,
$this->page,
$this->limit
);
It works totally fine when I want to sort by one of Service columns but I want to sort it by Service->getType()->getName() property.
Service has type property which is ManyToOne relation to ServiceType.
ServiceType uses DoctrineBehaviors Translatable, so column name belongs to other object named ServiceTypeTranslation.
I'd like to use its full functionality without having to write DQL myself, but when I try to name sort like "type.name" I get error that this column doesn't exist.
Eager loading wouldn't help as it only joins 1 table and translation for "name" property resides 2 tables deep from Service.
I know that I could try using $options array as 4th argument for paginate() but I can't get it to work.
Any ideas? Below some parts of Entities for visualisation.
Service.php
/**
* #ORM\Table
* #ORM\Entity
*/
class Service
{
...
/**
* #ORM\ManyToOne(targetEntity="DictServiceType", fetch="EAGER")
* #ORM\JoinColumn(referencedColumnName="id", nullable=false)
*/
private $type;
...
ServiceType.php
/**
* #ORM\Table
* #ORM\Entity
*/
class DictServiceType
{
use ORMBehaviors\Translatable\Translatable;
...
/**
* #return mixed
*/
public function getName()
{
return $this->translate(null, false)->getName();
}
...
ServiceTypeTranslation.php
/**
* #ORM\Table
* #ORM\Entity
*/
class DictServiceTypeTranslation
{
use ORMBehaviors\Translatable\Translation;
/**
* #var string
*
* #ORM\Column(type="string", length=255)
*/
private $name;
...
For the record, I tried to use paginator like this, based on what I read in the paginate() method description, but it's not even close to working.
There are no examples of how to use the $whitelist array anywhere, at least I couldn't find them.
$pagination = $this->get('knp_paginator')->paginate(
$this->list,
$this->page,
$this->limit,
array(
array(
$this->sort
)
)
);
And the phpdoc for this 4th argument says:
* #param array $options - less used options:
* boolean $distinct - default true for distinction of results
* string $alias - pagination alias, default none
* array $whitelist - sortable whitelist for target fields being paginated
i would like to define new class with extends of PDOStatement and in this child class i need override function bindColumn (PDOStatement::bindColumn) and in this override function call parent::bindColumn(). But i can't find default value for this method. Is there way to find default value in any method which i would like to override? In source code or somewhere? I need override more function and i would like to find any default value. Thanks
class myStatement extends PDOStatement
{
public function bindColumn ($column, &$param, $type = ?, $maxlen = ?, $driverdata = ?)
{
}
}
The only reason why I'm posting this as answers is that I can't post it as a comment.
/**
* (PHP 5 >= 5.1.0, PECL pdo >= 0.1.0)<br/>
* Bind a column to a PHP variable
* #link http://php.net/manual/en/pdostatement.bindcolumn.php
* #param mixed $column <p>
* Number of the column (1-indexed) or name of the column in the result set.
* If using the column name, be aware that the name should match the
* case of the column, as returned by the driver.
* </p>
* #param mixed $param <p>
* Name of the PHP variable to which the column will be bound.
* </p>
* #param int $type [optional] <p>
* Data type of the parameter, specified by the PDO::PARAM_* constants.
* </p>
* #param int $maxlen [optional] <p>
* A hint for pre-allocation.
* </p>
* #param mixed $driverdata [optional] <p>
* Optional parameter(s) for the driver.
* </p>
* #return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
*/
public function bindColumn ($column, &$param, $type = null, $maxlen = null, $driverdata = null) {}
Get yourself a decent IDE man, it works wonders.
The signature of this method is:
public function bindColumn ($column, &$param, $type = null, $maxlen = null, $driverdata = null) {}
You can use modern IDE like PHPStorm for example to find the signature of any method and other useful information
I am using the FOSRestBundle and was wondering is it possible to validate against empty query parameters using annotations?
For example when calling: /comments/1 an exception is thrown since both dealId and source query parameters haven't been set.
However calling /comments/1?dealId=1&source= is fine even though the source value hasn't ben set and doesn't match the regex outlined in the annotation.
Controller function:
/**
* Get a single comment.
*
* #Annotations\QueryParam(name="dealId", requirements="\d+", strict=true, description="The deal the comments belong to.")
* #Annotations\QueryParam(name="source", requirements="(forum|blog)", strict=true, description="The source of the comments.")
*
* #Annotations\View()
*
* #Annotations\Get("/comments/{id}", requirements={"id" = "\d+"})
*
*/
public function getCommentAction(Request $request, ParamFetcherInterface $paramFetcher, $id)
{
$dealId = $paramFetcher->get('dealId');
$source = $paramFetcher->get('source');
// TODO: Implement
return [ 'id' => $id, 'dealId' => $dealId, 'source' => $source ];
}
Update
I raised this issue on the FOSRestBundle's GitHub repo too and it looks as if what I am asking for is currently not possible due to the limitations of the Regex validator that is being used.
https://github.com/FriendsOfSymfony/FOSRestBundle/issues/814#issuecomment-49696288
If you want to force your parameters to be checked, you can change config file as explained in the documentation, Here is the sample:
fos_rest: param_fetcher_listener: force
Then you can set other options like strict, nullable accordingly.
See more details here :
http://symfony.com/doc/current/bundles/FOSRestBundle/configuration-reference.html (archive.org)
https://symfony.com/doc/3.x/bundles/FOSRestBundle/index.html#config-reference
https://symfony.com/doc/3.x/bundles/FOSRestBundle/annotations-reference.html
Just use the allowBlank option of the QueryParam. In your case you would set the allowBlank to false to get the expected behaviour:
The allowBlank option is NOT YET in the FOSRestBundle, but I provided a patch to the FOSRestBundle which has a good chance to land in the next release, version 1.5.0 of the bundle.
This is how your Controller would look like:
/**
* Get a single comment.
*
* #Annotations\QueryParam(name="dealId", requirements="\d+", strict=true, description="The deal the comments belong to.")
* #Annotations\QueryParam(name="source", requirements="(forum|blog)", strict=true, allowBlank=false, description="The source of the comments.")
*
* #Annotations\View()
*
* #Annotations\Get("/comments/{id}", requirements={"id" = "\d+"})
*
*/
public function getCommentAction(Request $request, ParamFetcherInterface $paramFetcher, $id)
{
$dealId = $paramFetcher->get('dealId');
$source = $paramFetcher->get('source');
}
The tricky part is allowing source and dealId to be empty but I think it's possible by
adding these parameters to your route (so they must be specified in order to access the controller) and using a string prefix for each parameter (i.e. dealid_ and source_), so it's possible to specify an empty value.
You'll also need to modify the regex requirements to allow empty values.
/**
* Get a single comment.
*
* #Annotations\View()
* #Annotations\Get("/comments/{id}/dealid_{dealId}/source_{source}",
* requirements={"id" = "\d+", "dealId" = "\d*", "source" = "(forum|blog)*"})
*/
public function getCommentAction(Request $request,
ParamFetcherInterface $paramFetcher, $id, $dealId, $source)
{
return [ 'id' => $id, 'dealId' => $dealId, 'source' => $source ];
}
#Annotations\QueryParam expects a nullable parameter to be set (true or false) if the strict parameter is used. Try setting it.
I guess you want:
#Annotations\QueryParam(name="dealId", requirements="\d+", strict=true, nullable=false, description="The deal the comments belong to.")
#Annotations\QueryParam(name="source", requirements="(forum|blog)", strict=true, nullable=false, description="The source of the comments.")
Also read more about QueryParam in the docs.
I am not familiar with symfony, but I think a simple
$dealId = isset($dealId) ? $dealId : '';
Would help your problem