Edit a Maniphest task programatically


Hello all,

i am trying the following:

  • When saving Maniphest task A also edit Maniphest task B
    – Scenario: Task A has a “Blocked By” custom field which, when set to “Task B” should automatically edit the custom field “Blocks” of Task B to the value of Task A

I don’t want to do this with Conduit as i don’t want to need a Conduit API Token for this. So, is there any way to (at best easily) editing a custom field of Task B when saving Task A without Conduit?

Thanks for an answer.

Take care


Have you tried Herald?


Hey @avivey, thanks for your reply. I haven’t tried Herald yet, but as this should go into an extension which might be used by others i don’t want to have the user to set up some rules or anything. I was more thinking of using the applyApplicationTransactionInternalEffects function of my custom field or something like that. Or would this be a bad way? Can Herald rules be applied without the user actually have to add them first?

Thanks and take care


Maybe applyApplicationTransactionExternalEffects would make sense in some case.

In your case, it sounds like “blocking” is a relation between tasks, so maybe it would be better represented by an Edge, and have the fields read the edges.


Hello @avivey,

thank you for your suggestion. When i get it right saving it as an edge will make it NOT appear in the customFields section, right? As i would like it to be there.

In general i find it very complicated to work with custom fields when reading an object (Maniphest task for example), having to do something like:

$blocked_field_list = PhabricatorCustomField::getObjectFields(
$blocked_fields = $blocked_field_list->getFields();$blocked_field = $blocked_fields['ganttchart:blocks'];

to get a list and values of attached custom fields for the task. Anyway, back to the edit case. When doing something like:

$blocks_task = id(new ManiphestTaskQuery())

works just fine, why isn’t it possible to do something like

$blocks_task->setCustomField('blockedBy', array('PHID-xxx'));

also? That would make it so much easier. But when i understand you right there is no such thing or way at the moment, right? I also started to fiddle with the ManiphestEditEngine or mimicking the function of PhabricatorEditEngine when saving edit data and tried creating a list of $xactions myself but as you might figure without any real luck.

Thanks for your answer and take care


The edge will not automatically show up, you still need a CustomField, but the field will be backed by an Edge and its transaction will be an EdgeTransaction, which handles both directions of the edge.

When doing this:

$object = get_object(...);

You can’t do policy checks, can’t trigger Herald, and can’t publish a feed event for the modification, which is why this flow has been replaces with Transactions flow:

$object = $get_object();
$xactions[] = new SetTitleTransaction()
$engine->applyTransactions($object, $xactions);

which handles all those things.


Hello @avivey,

about the Edges: alright, got it, thanks for clarifying.

about the save() method: i knew there was a catch to it, again thanks for clarifying it. Also i am not sure if there is any way to make this work with custom fields, i have found none.

about the TransactionEngine part: so i guess that would be the best way to go if i don’t want to use edges (as i still want the blockedBy and inverse blocks fields to be custom tokenizer fields and don’t want to fiddle with another complexity level). I will have a look into the PhabricatorEditEngine class and methods, maybe i can figure it out how to make it work with one custom field only. If we assume my custom field has a fieldKey myextension:blockedBy , how would the resulting new SetXYZTransaction() then be? new setMyextensionBlockedBy() or something else? And would i have to add another class in my extension, for example a setMyextensionBlockedBy class?

One question still: is the applyApplicationTransactionInternalEffects method in my custom field class then the right place to go for such code or would you advice for another place/method?

Thanks again for your input and take care


I think you’ll have a CustomFieldTransaction with setFieldId() and setNewValue(), but I’m not sure - I’m not sure I’ve ever written any. It’s possible that you write a custom Transaction class that extends CustomFieldTransaction, or something similar. There should be a few examples in the code, in Maniphest and/or Differential.

applyApplicationTransactionInternalEffects is for effects on the object being modified; You want applyApplicationTransactionExternalEffects (note External), which is for effects on other objects (the other end of the relation).

There’s no way to make save() work with custom fields - save() is part of the ORM layer, and custom fields are separate objects in that level. SearchEngine and the Custom Field Engine bring those together at a higher level.


Hey @avivey,

thanks a lot for pointing me in the right direction and giving me a possible starting point for this. I will look into it and try to find a solution.

Also thank you for giving a much appreciated insight into the complexity of Phabricator and the way things work.

Take care

1 Like

Hey @avivey,

so, finally the first problem is solved: editing Task B when Task A is saved. BUT with that another problem arises and i can’t get my head around it.

I solved the “saving Task A edits Task B” thing as follows (values are just for example):

public function applyApplicationTransactionExternalEffects(
        PhabricatorApplicationTransaction $xaction)
        if ($this->getProxy()) {
return $this->getProxy()->applyApplicationTransactionExternalEffects($xaction);

public function applyBlocks(PhabricatorApplicationTransaction $xaction)
        $blocks_tasks_phids = json_decode($xaction->getNewValue());
        if (!empty($blocks_tasks_phids)) {
            foreach ($blocks_tasks_phids as $blocks_task_phid) {
                $blocks_task = id(new ManiphestTaskQuery())

                $blocks_template = new ManiphestTransaction();

                $blocks_edit_engine = $blocks_task->getApplicationTransactionEditor();

                $blocks_transaction = clone $blocks_template;
                $blocks_transaction->setMetadataValue('customfield:key', 'ganttchart:blocks');
                $blocks_transactions[] = $blocks_transaction;

                $blocks_content_source = PhabricatorContentSource::newForSource(
                $blocks_edit_engine->applyTransactions($blocks_task, $blocks_transactions);


That works and edits the blocks field of Task B. But as soon as i do it with $blocks_edit_engine->applyTransactions($blocks_task, $blocks_transactions); the changes i made in the blockedBy field of Task A are NOT saved. I see the same behaviour as soon as i put

$blocks_field_list = PhabricatorCustomField::getObjectFields(

somewhere in the applyBlocks function. Any chance you might know why this is happening as i of course need to get the old value of Task B of field blocks before editing it and if i can’t do applyTransactions() at all i can’t edit Task B.

Thanks again for your help


Sorry, I don’t know; I’ve literally never used applyExternalEffects.

You’re going to have to look for examples in the code base.


Hi @avivey,

alright, thanks anyways!

Take care