I'm new in Drupal 8 and I need to update the user when a node of a specific content type is created or updated. I found hook_entity_create but this hook acts when creating a new entity. Any solution?
There're 3 main hooks you can ustilise here:
hook_entity_insert: for when an entity is actually created
hook_entity_update: for when an entity is updated
hook_entity_delete: for when an entity is deleted
Note: Beware that this hook will be called on any entity so unless you intend to make whatever operation you're performing run on every entity, do something like this:
function yourmudolename_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
if ($entity instanceof \Path\to\your\EntityInterface){
// Your code here
}
}
Or better still use the entity type hook insead.
hook_ENTITY_TYPE_insert
hook_ENTITY_TYPE_update
hook_ENTITY_TYPE_delete
See here: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_ENTITY_TYPE_insert/8.5.x for details.
Related
Based on question and this answer, i confuesd what the event preUpdate() in combination with symfony and doctrine really does.
To be exact, i mean this line in the answer above:
$event->setNewValue('password', $user->getPassword());
Why i need the extra step of manually setting the new value?
I thought that doctrine handles the event and update the entity by itself without a call of e.g. flush?
Supportive of the thrown exception
Field "password" is not a valid field of the entity "...\UserBundle\Entity\User" in PreUpdateEventArgs.
when i use this, i don't understand why i need to modify the entity manually.
Extended info about the exception: it's only thrown when users logged in. Not when users are registered or edited.
The setNewValue() method can be used to change the value of fields that have been marked as updated. It doesn't work if, for whatever reason, the field you want to update isn't already changed.
Usually, setting the password to null is enough to add it to the changeset. In the event that it isn't, you can request the unit of work recompute the changes.
For example:
<?php
if ($event->hasChangedField('password')) {
$event->setNewValue('password', $newPassword);
}
else {
$em = $event->getEntityManager();
$uow = $em->getUnitOfWork();
$uow->recomputeSingleEntityChangeSet(
$em->getClassMetadata(User::class),
$entity
);
}
I currently have an entity which I would like to modify slightly upon load. This modification will be a one time change which will then be persisted in a new field along with the entity.
To clarify my current objective: The entity is a "Location" and forms part of a nested set. It has a name, lft/rgt values and an Id. One computationally expensive task I was performing with this entity was to fetch a full location path and display it as text. For example, with the location entity "Waterloo" I want to display as "Waterloo|London|United Kingdom". This involves traversing through the entire set (to the root node).
To reduce the cost of this I've created a new field on the Location entity that can be stamped with this value (and updated as/when the location (or any location within the tree) name is modified). Considering my application is in a live state I need to avoid running this as a one off process as it would incur quite an intensive one-time hit on the DB, instead I'd like to apply this update as and when each location (without that value) is loaded. I assumed Doctrine's postLoad event mechanism would be perfect for achieving this, however..
The Location entities are not loaded directly by my application, they will always be the inverse side of a relation. With this is mind, and the fact that doctrine's postLoad event:
Doesn't load (allow access to) any associated data
Is only fired for owning Entities
I have no way of gently making these modifications.
Anyone have any advice, or experience on this?
I was able to load the associated Location objects within the postLoad event by using the initializeObject() method on the Entity Manager.
/**
* Upon loading the object, if the location text isn't set, set it
* #param \Doctrine\ORM\Event\LifecycleEventArgs $args
*/
public function postLoad(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$this->em = $args->getEntityManager();
$entity = $args->getEntity();
if ($entity instanceof \Entities\Location)
{
if (is_null($entity->getPathText()))
{
$entity->setPathText("new value");
$this->em->flush($entity);
}
} elseif ($entity instanceof {parent Entity})
{
$location = $entity->getLocation();
// This triggers the postLoad event again, but this time with Location as the parent Entity
$this->em->initializeObject($location);
}
}
This is a fairly basic question about CakePHP, but since my knowledge of this framework is rather rusty, it is making me lose a lot of time.
I have a ManyToMany relation between Guest and Present. Whenever a new Guest is created and associated with a present, I would like to mark the Present as taken. If the present is already taken, some error should arise. The reason why I am not just declaring that a Guest hasMany Presents is because in the future things may change and more than one guest could associate to a present, so I prefer to avoid a Db migration.
My Guest::add() action looks like follows. It is called with a POST with the data of a new Guest and the id of an existing Present.
public function add() {
if ($this->request->is('post')) {
$id = $this->request->data['Present']['id'];
$this->Guest->create();
$present = $this->Guest->Present->findById($id);
if ($present['Present']['taken']) {
throw new ForbiddenException();
}
if ($this->Guest->save($this->request->data)) {
if ($this->Guest->Present->saveField('taken', true)) {
// Give the guest a uuid and proceed with a welcome message
$this->Guest->read();
$this->set('uuid', $this->Guest->data['Guest']['uuid']);
}
}
}
else {
throw new ForbiddenException();
}
}
What happens is that a new Guest is created (correct) and associated with the given present (correct) but when I save the taken field a new present is created instead of modifying the given one.
What is the correct way to proceed to update the current Present?
If it is of any help, I am using CakePHP 2.0
For obtaining the model data by the primary key it's better to use theIn addition read method:
$present = $this->Guest->Present->read(null, $id);
The read method sets the model's id attribute so that further calls to other methods affect the same data record, rather than creating a new one. This should solve the problem you are having.
Model callbacks tend to be better suited for these situations. You could add a beforeSave callback to the Guest class to checks if the present is already taken, and not allow the creation if it is. This way the model logic is left in the model layer and you don't need to do any extra work e.g. if the constraint has to be enforced also when existing Guests are saved, or created from different controllers or actions.
It sounds like the ID of the model you are trying to save is losing scope. You should be able to resolve your issue by updating your code:
...
if ($this->Guest->save($this->request->data)) {
$this->Guest->Present->id = $id;
if ($this->Guest->Present->saveField('taken', true)) {
...
I see that hook_node_type() allows me to intercept and modify newly-created or newly-modified node-types. But apparently, hook_node_type() is not triggered when the node's field-definitions are created or modified.
For example, when I create a node-type "my_bio", hook_node_type() gets triggered. But if I then add a field "my_photo" to the "my_bio" node-type, then hook_node_type() is not triggered.
In Drupal 6, is there a way to write a hook that effectively extends hook_node_type(), so that the hook gets called when fields in a node-type are added or changed?
Alternatively, is there a hook that gets called when any field-definition is added or changed?
More specifically, this is what I am trying to accomplish:
I have two custom formatters which are "mates": formatter_1 and formatter_2. When some field in a nodetype is added or modified, I check whether the field's formatter is formatter_1. If so, I then check whether the nodetype contains a "mate" for this field, i.e. a second field whose formatter is formatter_2. If not, I add a field-mate to this nodetype.
EDITED
To address the update to your question...
CCK has a hook that gets fired whenever an instance of a field is attached to a node type, or an instance that is already attached to a node type is updated. It's called hook_content_fieldapi(); documentation seems to be pretty sketchy but it's mentioned in the content.crud.inc file as having the following operations:
create instance
read instance
update instance
delete instance
The hook implementation would look something like:
function mymodule_content_fieldapi($op, $field) {
if ($op == 'create instance') {
if ($field->foo == 'bar') {
// Do something
}
}
}
As 'instances' essentially define the relationship between a node type and a field, this should be a good place to start what you're trying to do. I'd recommend dumping out the values of $field in the hook to see what variables you've got to work with.
I have a custom class that populates a controller's action parameters based on the typehint of the parameter. This works well for documents (using public properties and setters).
My aim is to make the controller simple:
function updateAction(Article $article)
{
$dm = new DocumentManager(); // code elsewhere
$dm->merge($article);
$dm->flush();
return $this->redirect('/article/' . $article->getId());
}
The problem is that the input supplying the fields to programatically populate the Article class doesn't contain all of the properties of an Article class (perhaps the edit form only contains Title and Content, but disregards Author, etc).
I was hoping that the presence of an ID would allow the document to be merged gracefully with what is currently in the database. However, any fields that are missing at the time of a merge will be removed from the document in the database.
Is there a way to update a document in such a way that only the fields that are present (non-null, I guess) are updated?
Rather than hitting the db twice - once for the find, and once for the update, you can use a FIND_AND_UPDATE query.and do it all in one step.
See this docs page for details: http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/find-and-update.html
It seems that a clean way would be to bind the model AFTER retrieving it from the database. Something along the lines of ASP.NET MVC's UpdateModel.
function updateAction($id)
{
$dm = new DocumentManager(); // code elsewhere
$article = $dm->getRepository('Article')->find($id);
$this->updateModel($article);
$dm->flush();
return $this->redirect('/article/' . $article->getId());
}
If there are any better suggestions, feel free to answer...