Here's a definition of the field for the main document:
/**
* #var ArrayCollection
* #MongoDB\ReferenceMany(
* targetDocument="Some\Namespace\Document\Reference",
* sort={"creationDate": "desc"},
* simple=true
* )
* #Expose
* #Groups({"Main"})
* #Type("ArrayCollection<Some\Namespace\Document\Reference>")
* #var \Some\Namespace\Document\Reference[]
*/
protected $references;
I tried to get a list of main documents and serialized them via JMS Serializer, but I found, that references is empty array. After some investigation, I discovered, that for getReferences, documents returns instance of PersistentCollection for which:
count returns 2 [ok]
getMongoData returns array of MongoIds [ok]
toArray returns empty array [invalid]
Looks like that's because of initialize method, that clears mongoData.
I achived the proper outcome with following code:
/**
* #VirtualProperty
* #SerializedName("reference_ids")
* #Groups("Main")
* #return array
*/
public function getReferenceIds()
{
$out = array();
foreach ($this->getReferences()->getMongoData() as $val) {
$out[] = (string)$val;
}
return $out;
}
But it's only a shortcut and I don't feel, that's a proper solution.
If anyone has an idea how to retrieve these ids or whole documents using PersistentCollection and why initialize method clears mongoData ?
Thanks.
Related
When I have a document property using a dbRef - relation like this:
/**
* #MongoDB\ReferenceOne (
* discriminatorMap={
* "post"=Post::class
* },
* defaultDiscriminatorValue="post",
* storeAs="dbRef",
* inversedBy="references",
* cascade={"persist"}
* )
*/
protected DocumentInterface $target;
How can I apply the built-in SearchFilter from Api_Platform to return all items with a matching dbRef and what would the adequate Request look like?
I have now implemented a workaround myself by creating a custom dbRef-Filter to add a filter for existing references to the AggregationBuilder.
$aggregationBuilder->match()->field($property)->references($document);
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)
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 have a many-to-many-relation, and when I load an entity that is on one side this relation, I expect to see as its property the ArrayCollection of related entities on another side. However, this does not happen - the ArrayCollection loaded has no elements in it, while in the database I can see the related entries. What could be the reason?
Here is my code:
One side of the relation, ConsolidatedReport class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports")
* #ORM\JoinTable(name="con_rprt_responses")
*/
private $responses;
Another side of the relation, Response class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\ConsolidatedReport\ConsolidatedReport", mappedBy="responses")
*/
private $consolidatedReports;
Here is the function I run to get an instance of ConsolidatedReport. This function sits inside a service that is being called from container:
/**
* Picks the consolidated report with given id.
*
* #param string $id
*
* #return ConsolidatedReport
*
* #throws NonExistentConsolidatedReportException if the survey doesn't exist
*/
public function pick($id)
{
$report = $this->repository->findOneBy(array('id' => $id));
if (!$report) {
throw new NonExistentConsolidatedReportException($id);
}
return $report;
}'
In the database, there is "con_rprt_responses" table with two columns "consolidated_reports_id" and "response_id". However, in profiler I do not see any queries to that table.
What could go wrong here?
UPDATE:
Please see my answer to this question below, that worked for me.
I added fetch="EAGER" to the $responses property of ConsolidatedReport class, and it worked.
The code now looks like this:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports", fetch="EAGER")
* #ORM\JoinTable(name="con_rprt_responses")
*/
private $responses;
More info here:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
Still if someone knows why the collection of related entity would not load without explicitly specifying EAGER fetching - please share your knowledge, it is highly appreciated!
If you specify the joinColumns, does this solve your problem?
/**
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports")
* #ORM\JoinTable(name="con_rprt_responses",
* joinColumns={#ORM\JoinColumn(name="consolidated_reports_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="response_id", referencedColumnName="id")}
* )
*/
The *toMany properties have to be initialized with an ArrayCollection.
public function __construct() {
$this->responses = new \Doctrine\Common\Collections\ArrayCollection();
$this-> consolidatedReports = new \Doctrine\Common\Collections\ArrayCollection();
}
In case you have more then single query to fetch the same objects using Doctrine try to use:
$entityManager->clear();
in between, to fix "missing" entities. It isn't solution "as is", however can give you an idea something wrong in chain of your queries.
Let's say I have object A.
/**
* #Table(name="a")
* repo class definition annotation here
**/
class A {
/**
* #Column(name="id")
* ... etc
**/
private $id;
/**
* this is pulled from another table. It is not an entity, just a decimal value
*/
private $otherThingFromAnotherTable;
}
How can I pull $otherThingFromAnotherTable from table other_table using ORM annotations? It's not an entity, it's just a decimal value.
I've got the following annotations, so far:
* #ORM\OneToOne(targetEntity="??")
* #ORM\JoinTable(name="other_table",
* joinColumns={
* #ORM\JoinColumn(name="offer_id", referencedColumnName="id")
* }
* )
Currently, I am just using raw queries in the repo class so I can use it through the entity manager. Unfortunately, that only returns an array, not the object. I'd like to be able to use just $this->em->repo->find($id) and have it pull everything automatically instead of a custom method.