Consider the following:
data class ChildState(
val name: Party,
override val linearId : UniqueIdentifier = UniqueIdentifier()
) : LinearState
data class ParentState(
val name : Party,
val children : LinearPointer<ChildState>,
override val linearId : UniqueIdentifier = UniqueIdentifier()
) : LinearState
Can I somehow, as this Mathew Lyton post seems to suggest, obtain the child state querying just the parent from outside the module using CordaRPCOps? Somehting like this:
// rpc.proxy is a NodeRPCConnection containing the CordaRPCOps
val parentStateData = rpc.proxy.vaultQueryBy<ParentState>().states.single().state.data
// perhaps something like this?
parentStateData.children.resolve(rpc.proxy)
The default implementation of StatePointer in Corda doesn't support resolving a pointer using the CordaRPCOps. It can only be resolved using the ServiceHub or the LedgerTransation. Below are the methods defined in the StatePointer class in Corda.
/**
* Resolves a [StatePointer] to a [StateAndRef] via a vault query. This method will either return a [StateAndRef]
* or return an exception.
*
* #param services a [ServiceHub] implementation is required to resolve the pointer.
*/
#DeleteForDJVM
abstract fun resolve(services: ServiceHub): StateAndRef<T>
/**
* Resolves a [StatePointer] to a [StateAndRef] from inside a [LedgerTransaction]. The intuition here is that all
* of the pointed-to states will be included in the transaction as reference states.
*
* #param ltx the [LedgerTransaction] containing the [pointer] and pointed-to states.
*/
abstract fun resolve(ltx: LedgerTransaction): StateAndRef<T>
Neither the ServiceHub not the LedgerTransaction is accessible in the rpc client, hence in order to resolve the pointer using the CordaRPCOps, you will need to write a custom implementation as described in the blog you mentioned.
Related
If I have a variable of a type ArrayCollection how do I check if a key of a specific name exists in the collection including nesting. And if it does how do I get and change that value?
I guess you are talking about Doctrines ArrayCollection \Doctrine\Common\Collections\ArrayCollection.
It does implement phps native ArrayAccess interface, so have a look at the docs. Just check like:
use Doctrine\Common\Collections\ArrayCollection;
$myCollection = new ArrayCollection(array('testKey' => 'testVal'));
var_dump(isset($myCollection['testKey']));
It does also implement its own method from the Collection interface.
/**
* Checks whether the collection contains an element with the specified key/index.
*
* #param string|integer $key The key/index to check for.
*
* #return boolean TRUE if the collection contains an element with the specified key/index,
* FALSE otherwise.
*/
public function containsKey($key);
For nested objects there is no build in method, you have to traverse the collection yourself like you would do with a normal array.
The way that I found was to use the toArray() method in the ArrayCollection object and then use the array_search function:
$newArray = $arrayCollectionObject->toArray();
$keyThatIneed = array_search($value, $newArray);
I'm currently working on a project that the previous developer integrated with JMSTranslationBundle.
At this moment, I've did some modification to the application, one of them was to turn the menu to be highly dynamic. (Basically, the user logic of the application have 3 layers and each layer have their own menu).
The menu are stored into the database and accessible through the doctrine entity. To display the label, I store into the DB the "label code" which is used by JMSTranslationBundle as a key to identify it. The desc is by default empty until setted into the translation file. (editable with the _trans route).
Into the documentation of JMS, it is mentionned that one can implement TranslationContainerInterface so when the compilation of the translation file (who are XLIFF file currently) are done, each class implementing this will be called to return a list of Message objects. Here's my issue:
The function to implement is static, meaning that when call, my model Menu (who handle to logic of fetching throught Doctrine repo) is not loaded via the service manager. This means that I do not receive the repository object (since it's loaded by service and pass through the controller):
public function __construct(MenuRepository $objMenuRepo)...
The definition of the function I implements is:
static function getTranslationMessages(){ ... }
My question is: how can I obtain the doctrine (either manager or repository) within that static function . (Since this will be only called on translation initial generation and not by the site itsef, performance is not an issue I worry about).
Also: If anyone have better alternative to propose (that wouldn't involved getting rid of this translation bundle, trust me, it would take quite an amount of time right now), I'm opened to hear them.
Thank you :-)
If some of you are interested, I had to use an alternative solution.
Although it doesn't answer the question on how to use a service within a static context, it will help those who ran into the same issue I had when attempting to implement with JMSTranslation.
To implement the solution (to extract translation key from the database), I had to use the JMS\TranslationBundle\Translation\ExtractorInterface.
I have implement it under this format:
class TranslationRepositoriesExtractor implements ExtractorInterface{
//Loaded through the service container
public function __construct(EntityRepository $objRepositoryNeeded);
// Implementation of the interface ExtractorInterface.
// Within this function, I've used the EntityRepository received in the
// constructor to fetch the list of keys that would be use for translating
/**
* #return \JMS\TranslationBundle\Model\Message[]
*/
public function extract()
}
As you can notice, the extract function return an array of \JMS\TranslationBundle\Model\Message.
After implementing this function, you have to add your object as a service and make it recognizable by JMSTranslationBundle as an extractor. To do so:
<!-- Replace the id of the service, the class path, the id of the argument and the alias
named you want by the value you need in your application -->
<service id="idOrYourService" class="Path\Of\Class\TranslationRepositoriesExtractor">
<argument type="service" id="repository.needed" />
<tag name="jms_translation.extractor" alias="NameOfAlias" />
</service>
The alias tag is used within JMSTranslationBundle to recognize your class as an extractor.
Finally, when generating the files, I had to had to enable the extractor. This can be done via the config, but in my case, was done manually through the command line
php app/console translation:extract --enable-extractor=NameOfAlias en
// NameOfAlias is the same name as the one defined in the tag of your service
I hope I didn't forget any step (if so, feel free to reply in a comment and I'll update the answer).
Happy coding :-)
Using this input, I ended up coding this version of the extractor.
<?php
namespace MyBundle\Service;
use Doctrine\ORM\EntityManager;
use JMS\TranslationBundle\Model\Message;
use JMS\TranslationBundle\Model\MessageCatalogue;
use JMS\TranslationBundle\Translation\ExtractorInterface;
/**
* Extracts translatable strings from Doctrine entities
*
* #package MyBundle\Service
*/
class EntityTranslationExtractor implements ExtractorInterface
{
/**
* #var EntityManager
*/
private $entityManager;
/**
* EntityTranslationExtractor constructor.
*
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* #return MessageCatalogue
*/
public function extract()
{
$messageCatalogue = new MessageCatalogue();
// Sample portion of the extraction
$translatableEntities = $this->entityManager->getRepository('MyBundle:MyEntity')->findAll();
foreach ($translatableEntities as $entity) {
$message = new Message($entity::class .'.'. $entity->getName(). '.name');
$message->setDesc(ucwords($entity->getName()));
$messageCatalogue->add($message);
}
return $messageCatalogue;
}
}
As the title says, I am trying to make a run-time decision on whether or not to include fields in the serialization. In my case, this decision will be based on permissions.
I am using Symfony 2, so what I'm looking to do is add an additional annotation called #ExcludeIf which accepts a security expression.
I can handle the annotation parsing and storing of the meta data, but I am not able to see how to integrate a custom exclusion strategy with the library.
Any suggestions?
Note: exclusion strategies are an actual construct in the JMS codebase, I just haven't been able to figure out the best way to integrate an extra on top of the others
PS: I had asked about this before and was pointed to using groups. For various reasons this is a very poor solution for my needs.
You just have to create a class that implements JMS\Serializer\Exclusion\ExclusionStrategyInterface
<?php
namespace JMS\Serializer\Exclusion;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Context;
interface ExclusionStrategyInterface
{
/**
* Whether the class should be skipped.
*
* #param ClassMetadata $metadata
*
* #return boolean
*/
public function shouldSkipClass(ClassMetadata $metadata, Context $context);
/**
* Whether the property should be skipped.
*
* #param PropertyMetadata $property
*
* #return boolean
*/
public function shouldSkipProperty(PropertyMetadata $property, Context $context);
}
In your case, you can implement your own custom logic in the shouldSkipProperty method and always return false for shouldSkipClass.
Example of implementation can be found in the JMS/Serializer repository
We will reference the created service as acme.my_exclusion_strategy_service below.
In your controller action:
<?php
use Symfony\Component\HttpFoundation\Response;
use JMS\Serializer\SerializationContext;
// ....
$context = SerializationContext::create()
->addExclusionStrategy($this->get('acme.my_exclusion_strategy_service'));
$serial = $this->get('jms_serializer')->serialize($object, 'json', $context);
return new Response($serial, Response::HTTP_OK, array('Content-Type' => 'application/json'));
Or if you are using FOSRestBundle
<?php
use FOS\RestBundle\View;
use JMS\Serializer\SerializationContext;
// ....
$context = SerializationContext::create()
->addExclusionStrategy($this->get('acme.my_exclusion_strategy_service'))
$view = new View($object);
$view->setSerializationContext($context);
// or you can create your own view factory that handles the creation
// of the context for you
return $this->get('fos_rest.view_handler')->handle($view);
As of jms/serializer 1.4.0, the symfony expression language is integrated in its core.
The feature is documented at http://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies#dynamic-exclusion-strategy and this allows to use runtime exclusion strategies.
An example taken from the documentation is:
class MyObject
{
/**
* #Exclude(if="service('user_manager_service').getSomeRuntimeData(object)")
*/
private $name;
/**
* #Expose(if="service('request_stack').getCurrent().has('foo')")
*/
private $name2;
}
I this example, the services user_manager_service and request_stack are invoked at runtime, and depending on the return (true or false), the property will be exposed or not.
With the same expression language, as of 1.6.0 is possible also to use virtual properties via expression language.
Documented at http://jmsyst.com/libs/serializer/master/reference/annotations#virtualproperty allows to add on the fly data coming from external services
I have a tree of Employee objects (they are in a tree-like hierarchy, with everyone having one leader, and all leaders having more employees). All the Employees have a integer parameter called units.
/**
* #ORM\Entity
* #ORM\Table(name="employees")
*/
class Employee
{
/**
* #ORM\Id
* #ORM\Column(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Employee", mappedBy="leader")
*/
protected $employees;
/**
* #ORM\ManyToOne(targetEntity("Employee", inversedBy="employees")
*/
protected $leader;
}
I need to get all the employees, who have at most N units, where N is defined in config.yml. At first, I was trying to push $configContainer into $GLOBALS, and use it in ArrayCollection::filter()'s Closure. Now I found a method, so I can use variables in the Closure:
public function getBestEmployees(&$configContainer)
{
return $this->getAllEmployees()->filter(
function bestEmployees($employee) use ($configContainer)
{
return ($employee->getUnits() >= $configContainer->getParameter('best_unit_count'));
}
);
}
Now I wonder if there is any other way to access the configuration parameters from an Entity, or do I really have to pass the whole configContainer as a reference? Or am I doing it totally wrong?
You shouldn't be accessing the service container at all inside entities. The value itself should be passed instead
public function getBestEmployees($bestUnitCount)
{
return $this->getAllEmployees()->filter(function ($employee) use ($bestUnitCount) {
return $employee->getUnits()->count() >= $bestUnitCount;
});
}
Of course, we haven't actually solved the problem yet: the parameter still needs to be fetched from the container somewhere. If this method gets invoked mostly in controller actions, I wouldn't bother doing any extra work to make things cleaner and would pass the container parameter straight in the controller action.
However, should there be a need to get the best employees in a Twig template, for example, it would be nice if it wouldn't be necessary to pass the parameter. One possibility would be using a setter method and passing the parameter down beforehand to each and every entity that gets retrieved from the database. You could do this either in repositories or entitiy managers. The most advanced solution would be to listen to the postLoad event and pass the parameter in an event listener.
I'm building a small website with Symfony2 and Doctrine2. There are blog posts, events and press releases. Each of these is so similar that I decided to use single table inheritance (STI) with a parent table called 'Node'.
Nodes have:
a 'published' field, which is boolean
a 'locale' field, which is a string and says 'this is only to be shown in this locale'. (The locale is passed in via the request).
By default, I only want to display published nodes that are from the current locale.
Obviously, I could create lots of queries in the repository that look something like:
$this->createQueryBuilder('Event')
->where('Node.published = 1')
->where('Node.locale = :locale')
but this doesn't seem very DRY.
So how do I build a default query which other queries can 'inherit' from? This should include default Doctrine queries based on relations.
Inheritance is probably overkill.
Why not just create a little factory method that gives you a preconfigured queryBuilder?
class NodeRepository extends Doctrine\ORM\EntityRepository {
public function getLocalizedNodeQueryBuilder($type,$locale){
return $this->getQueryBuilder($type)
->where('Node.published = 1')
->where('Node.locale = :locale')
->setParameter('locale',$locale);
}
}
You could do the same thing, and simply override getQueryBuilder if you're sure you always want your querybuilder configured that way.
You don't need to build anything like that into your repository classes. If you set up single table inheritance with a "Discriminator Map" you'll end up with seperate classes (Entities). Doctrine will take care of filtering by your "node type" when it interacts with the DBAL.
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/inheritance-mapping.html#single-table-inheritance
For example..
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"node" = "Node", "blogpost" = "Blogpost", "events" = "Events"})
*/
class Node
{
// ...
}
/**
* #Entity
*/
class Blogpost extends Node
{
// ...
}
/**
* #Entity
*/
class Events extends Node
{
// ...
}
// get me all the blogposts
$blogposts = $em->getRepository('Blogpost')->findAll();
// get me all the events
$events = $em->getRepository('Events')->findAll();
This is especially beneficially as you'll be able to encapsulate your "Blogpost" logic into its own entity, rather than trying to represent it with its parent class "Node".