In drupal 8, is it possible to create complex configuration entities with reference entities? For example, to link multiple node in a configuration entities field? I haven't found documentation...
I'm not sure I understand your question but I will give you some clues to resolve your issue.
Sorry if my answers doesn't fit you needs.
Do you want only a configuration field linking multiple nodes ?
or
Do you want a custom Configuration Entity ?
Configuration field
Use the field entity_autocomplete would help you.
Extra documentation here: https://www.drupal.org/node/2418529
Usage:
/**
* #file
* Contains A custom Class Form.
*/
// ... Namespaces & uses.
class AdminForm extends FormBase {
// Class definitions
public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL) {
$form['node_collection'] = array(
'#type' => 'entity_autocomplete',
'#target_type' => 'node',
'#title' => 'Nodes',
);
}
}
You should then save it in the State or the Config API.
Check this article for the difference between both (it will help you to choose the correct one): https://antistatique.net/en/we/blog/2016/06/14/drupal-8-differences-between-configuration-api-state-api
Configuration entity
You should take a look at this documentation about Configuration Entity:
https://www.drupal.org/docs/8/api/configuration-api/creating-a-configuration-entity-type-in-drupal-8
Then, you could look this answer, It will help you to create an entity_reference field for node or any entity you want.
Retrieve a taxonomy term in the buildrow function of a drupal 8 custom entity
Hope it will help you !
Related
I want validate an entity doctrine differently when the entity is created, updated or deleted.
There is an entity constraint validator in my entity class.
// src/AppBundle/Entity/AcmeEntity.php
use AppBundle\Validator\Constraints as AcmeAssert;
/**
* #AcmeAssert\CustomConstraint
*/
class AcmeEntity
{
// ...
protected $name;
// ...
}
In my CustomConstraint I want determine if the Entity will be updated, created or delete for execute a specific validator.
Using unit of work is a solution ?
What is the best way to make this?
I think this problematic is common in lot of application ?
Thank's all ;)
You could either use validation groups based on the submitted data or handle itwhen you create the form by passing the validation group.
For example, in your controller when you create the form;
$form = $this->createForm(new AcmeType(), $acme, ['validation_groups' => ['create']]);
Then you entity would be something like;
/**
* Get name
*
* #Assert\Length(min=2, max=11, groups={"create", "update"})
* #AcmeAssert\ContainsAlphanumeric(groups={"create"}) // only applied when create group is passed
* #return string
*/
public function getName()
{
return $this->name;
}
This is what validation groups are made for.
Since Symfony Forms read validations from entity annotations and use internally the Validator component you'd have a look at these articles in the documentation:
http://symfony.com/doc/current/form/validation_groups.html
http://symfony.com/doc/current/validation/groups.html
http://symfony.com/doc/current/validation/sequence_provider.html
I am using Doctrine inheritance mapping in a project which produces a set of unique entities that each extend a base entity. Because the route is not aware of which entities go with which base rows, I have to query the database twice in order to grab the row I want from the right fieldset:
// in a controller action:
// locate the event entity record and determine the event type
$entity = 'AdminEvents\Entity\Event';
$event = $this->getEntityManager()->find($entity, $eventID);
$eventType = $this->getEntityManager()->getClassMetadata(get_class($event))->discriminatorValue;
// locate the record we're really looking for in the unique extended entity
$entity = 'AdminEvents\Entity\\' . $eventType;
$event = $this->getEntityManager()->find($entity, $eventID);
Is there a cleaner way to do this?
You should probably define an \AdminEvents\Entity\AbsractEvent class, if you haven't already. Then each of your STI entities should extend this, and you can do instanceof (or other logic) to find out what concrete type you got:
// locate the record using the AbstractEntity
$entity = 'AdminEvents\Entity\AbstractEntity';
$event = $this->getEntityManager()->find($entity, $eventID);
A word of caution: the SPL function, get_class will often return the Doctrine Proxy class, so don't rely on that directly to test the return type. You can use the Doctrine class 'ClassUtils'
\Doctrine\Common\Util\ClassUtils::getRealClass(get_class($event));
I've just started with unit testing in CakePHP (yay!) and ran into the following challenge. Hope somebody can help me :-)
Situation
My model uses a Behavior to send changes to an API after saving it locally. I would like to fake all calls made to the API during the test (those will be tested seperately) to save load on the API server, and more important, not actually save the changes :-)
I'm using CakePHP 2.4.1.
What I've tried
Read the docs. The manual shows how to do this for Components and Helpers but not for Behaviors.
Google. What I've found:
A Google Group post which says it simply "isn't possible". I don't take no for an answer.
An article explaining how to mock an object. Comes pretty close.
The code from the article reads:
$provider = $this->getMock('OurProvider', array('getInfo'));
$provider->expects($this->any())
->method('getInfo')
->will($this->returnValue('200'));
It might be the wrong direction, but I think that might be a good start.
What I want
Effectively: A snippet of code to demo how to mock a behavior in a CakePHP Model for unit testing purposes.
Maybe this question will result in an addition of the CakePHP manual too as an added bonus, since I feel it's missing in there.
Thanks in advance for the effort!
Update (2013-11-07)
I've found this related question, which should answer this question (partly). No need to mock up the API, instead I can create a Behavior test that the model will use.
I'm trying to figure out what that BehaviorTest should look like.
Use the class registry
As with many classes, behaviors are added to the class registry using the class name as the key, and for subsequent requests for the same object loaded from the classregistry. Therefore, the way to mock a behavior is simply to put it in the class registry before using it.
Full Example:
<?php
App::uses('AppModel', 'Model');
class Example extends AppModel {
}
class TestBehavior extends ModelBehavior {
public function foo() {
throw new \Exception('Real method called');
}
}
class BehaviorExampleTest extends CakeTestCase {
/**
* testNormalBehavior
*
* #expectedException Exception
* #expectedExceptionMessage Real method called
* #return void
*/
public function testNormalBehavior() {
$model = ClassRegistry::init('Example');
$model->Behaviors->attach('Test');
$this->assertInstanceOf('TestBehavior', $model->Behaviors->Test);
$this->assertSame('TestBehavior', get_class($model->Behaviors->Test));
$this->assertSame(['foo' => ['Test', 'foo']], $model->Behaviors->methods());
$model->foo();
}
public function testMockedBehavior() {
$mockedBehavior = $this->getMock('TestBehavior', ['foo', 'bar']);
ClassRegistry::addObject('TestBehavior', $mockedBehavior);
$model = ClassRegistry::init('Example');
$model->Behaviors->attach('Test');
$this->assertInstanceOf('TestBehavior', $model->Behaviors->Test);
$this->assertNotSame('TestBehavior', get_class($model->Behaviors->Test));
$expected = [
'foo' => ['Test', 'foo'],
'bar' => ['Test', 'bar'],
'expects' => ['Test', 'expects'], // noise, due to being a mock
'staticExpects' => ['Test', 'staticExpects'], // noise, due to being a mock
];
$this->assertSame($expected, $model->Behaviors->methods());
$model->foo(); // no exception thrown
$mockedBehavior
->expects($this->once())
->method('bar')
->will($this->returnValue('something special'));
$return = $model->bar();
$this->assertSame('something special', $return);
}
}
I have been wondering why my mappings and the controller actions are not working. For this I need to refer to my previous post, where I have described my entities and database schema, which can be found here. I need to start a new post since there were no further updates and I thought this the only way to get attention of the Doctrine + Zend Pros.
As described in my previous post, I have a Zend Form, the user can enter teamId and teamName, further he has the choice to select multiple players from the drop down list on the form and can allocate players to the team. So basically that is my goal to achieve with Doctrine and Zend. For that I wrote my entities described in the previous post and right now I want to add the code from my controller to persist the entities.
Controller:
public function addAction()
{
$form = new TeamForm($this->getEntityManager());
$form->get('submit')->setAttribute('value', 'Add');
$request = $this->getRequest();
if ($request->isPost())
{
$team = new Team();
$player = new Player();
$teamPlayers = new TeamPlayer();
$form->setInputFilter($typeset->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid())
{
$team->populate($form->getData());
$teamPlayers->setPlayer($player);
$teamPlayers->setTeam($team);
$this->getEntityManager()->persist($teamPlayers);
$this->getEntityManager()->flush();
//Reroute to the index page once the data is successfully added
}
}
//return form array
return array(
'form' => $form
);
}
So that is basically what Im doing in my controller to save the entities into two tables (team Table and teamPlayer Table), as already the player Table is populated with data. So I want to add players to the team and assign that values in these two tables.
Right now I can see my form and when I enter the data and press submit nothing is happening, i can see the form with no action. When the data is successfully saved into the database then I reroute it to the index page which is not happening.
Any help would be appreciated, to point out the error Im making either in mapping section or in the controller side.
The official documentation especially from Doctrine 2 is too global and is particulary not that clear for my requirement.
let's try to solve it by updating this answer by steps :
step 1 :
your words implied to me that , you might have some issues in from validation , so lets check if you are passing this $form->isValid()
if ($form->isValid())
{
$team->populate($form->getData());
$teamPlayers->setPlayer($player);
$teamPlayers->setTeam($team);
$this->getEntityManager()->persist($teamPlayers);
$this->getEntityManager()->flush();
//Reroute to the index page once the data is successfully added
}else{
var_dump($form->getMessages());
}
I can also suggest you to use doctrine commandline : doctrine orm:validate-schema , this command will help you to check if your entity mapping is ok plus your database mapping is also okay , i see it as handy to to debug your doctrine2 entity
ps : I havn't read your entities in depth yet
i tried to implement the file upload via doctrine/lifecycle callbacks as described here:
http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html#using-lifecycle-callbacks
So far it works, but the PrePersist/PreUpdate Event is not fired, the function "preUpload" is not called.
Functions like "upload" and "removeUpload" triggered by other lifecycle events are called correctly.
Does anyone have an idea why the event is not fired or a solution for this problem?
Thanks
I have another solution to this problem:
My entity has a field "updatedAt" which is a timestamp of the last update. Since this field gets set anyway (by the timestampable extension of Gedmo) I just use this field to trick doctrine into believing that the entitiy was updated.
Before I persist the entity I set this field manually doing
if( $editForm['file']->getData() )
$entity->setUpdateAt(new \DateTime());
This way the entity gets persisted (because it has changed) and the preUpdate and postUpdate functions are called properly.
Of course this only works if your entity has a field that you can exploit like that.
You need to change tracking policies.
Full explanation.
there's a much simpler solution compared with changing tracking policies and other solutions:
in controller:
if ($form->isValid()) {
...
if ($form->get('file')->getData() != NULL) {//user have uploaded a new file
$file = $form->get('file')->getData();//get 'UploadedFile' object
$news->setPath($file->getClientOriginalName());//change field that holds file's path in db to a temporary value,i.e original file name uploaded by user
}
...
}
this way you have changed a persisted field (here it is path field), so PreUpdate() & PostUpdate() are triggered then you should change path field value to any thing you like (i.e timestamp) in PreUpdate() function so in the end correct value is persisted to DB.
A trick could be to modify the entity no matter what..on postLoad.
1 Create an updatedAt field.
/**
* Date/Time of the update
*
* #var \Datetime
* #ORM\Column(name="updated_at", type="datetime")
*/
private $updatedAt;
2 Create a postLoad() function that will modify your entity anyway:
/**
* #ORM\PostLoad()
*/
public function postLoad()
{
$this->updatedAt = new \DateTime();
}
3 Just update that field correctly on prePersist:
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
$this->updatedAt = new \DateTime();
//...update your picture
}
This is basically a slight variation of #philipphoffmann's answer:
What i do is that i modify an attribute before persisting to trigger the preUpdate event, then i undo this modification in the listener:
$entity->setToken($entity->getToken()."_tmp");
$em->flush();
In my listener:
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof MyEntity) {
$entity->setToken(str_replace('_tmp', '', $entity->getToken()));
//...
}
}
Another option is to display the database field where the filename is stored as a hidden input field and when the file upload input changes set that to empty so it ends up triggering doctrine's update events. So in the form builder you could have something like this:
->add('path', 'text', array('required' => false,'label' => 'Photo file name', 'attr' => array('class' => 'invisible')))
->add('file', 'file', array('label' => 'Photo', 'attr' => array('class' => 'uploader','data-target' => 'iddp_rorschachbundle_institutiontype_path')))
Path is a property managed by doctrine (equal to the field name in the db table) and file is the virtual property to handle uploads (not managed by doctrine). The css class simply sets the display to none. And then a simple js to change the value of the hidden input field
$('.uploader').change(function(){
var t = $(this).attr('data-target');
//clear input value
$("#"+t).val('');
});
For me, it worked good when I just manually called these methods in the controller.
Do you have checked your metadata cache driver option in your config.yml file?If it exists, just try to comment this line:
metadata_cache_driver: whateverTheStorage
Like this:
#metadata_cache_driver: whateverTheStorage