Updating a block programmatically

Permalink
I would like to edit a blocks single value.

Intention for this functionality is to load the block within a loop extract the data values, amend a single or couple of values and then update the block.

In the following case I'm attempting to update links to use a relative locale link if it exists instead of the current link.

I've been looking at this and got the following code, but currently running this wipes the other values out, I know I'm missing a glaring piece of the puzzle but could do with some help pointing it out.

<?php
$blocks = $page->getBlocks();
$nvc = $page->getVersionToModify();
$isApproved = $page->getVersionObject()->isApproved();
foreach ($blocks as $b) {
    $blockController = $b->getController();
    $data = [];
    //Get an array of the exported keys that could be internal collection ID's
    $exportedKeys = $blockController->getBlockTypeExportPageColumns();
    if (!count($exportedKeys) > 0) {
        return;
    }
    $language = Section::getBySectionOfSite($page);
    if (is_object($language)) {
        foreach ($exportedKeys as $key) {


::Edit::
I'm looking into the rescan locale method to try and include more than just the content block
\Concrete\Core\Multilingual\Page\Section\Processor\ReplaceContentLinksTask::13
FR over here -https://github.com/concrete5/concrete5/issues/6286...

TheRealSean
 
mnakalay replied on at Permalink Reply
mnakalay
Hello,

You can to modify your loop into $exportedKeys like this:

foreach ($exportedKeys as $key) {
    //retrieve each key from the blockController
    $value = $blockController->get($key);
    if ($key === 'internalCID' && (int) $value > 0) {
        $link = Page::getByID($value, 'ACTIVE');
        $relatedCID = $language->getTranslatedPageID($link);
        if ($relatedCID) {
            $value = $relatedCID;
        }
    }
    $data[$key] = $value;
}


Also, this line looks weird to me
if (!count($data) > 0) {


The ! probably shouldn't be there and I would say that with your $data now being full anyway, a better check to decide whether to save or not would be to check if $relatedCID has a value since that's the only value you really want to change

So probably do
if (isset($relatedCID) && $relatedCID) {
     // your saving here
}
TheRealSean replied on at Permalink Reply
TheRealSean
Thanks mnakalay I'll have a go at refactoring the above.
Good spot, the intention with the data check was to only submit if the data array was not empty (sometimes it is). I only want to update values that have been amended(which may not be possible)

I need to be a little more flexible with the key 'internalLinkCID' as some of the blocks in my instance have multiple internal variables.

My block saving here is wrong as well, I had a quick play but running update the way I do deletes all the extra tables associated with the block :(
TheRealSean replied on at Permalink Reply
TheRealSean
I'm still looking for help with this.

I want to save a blocks data after only modifying a single variable
Juha replied on at Permalink Reply
Juha
I don't know if I'm looking at this correctly, but it looks like the only things you have in $data are the $relatedCID values, correct? And when looking at C5 source it also looks like running Block::update() runs the save() method of the block type controller.

So in case of Image block (for example) that would be \Concrete\Block\Image\Controller::save(), which expects the $args parameter (your $data) to contain all the information it needs, not just the "internalLinkCID".

Or did I miss something?
TheRealSean replied on at Permalink Reply
TheRealSean
Yes I was/am struggling to get the data out in a format I can use.

The intention (I'm still trying to fully work out) is to scan a block work out what fields would need updating. Load the data, edit the relevant fields and then save the data back into the block.

This is what I currently have and at this stage pulling the data out (as an object)

:notice: this is not working don't try to use it
$blocks = $c->getBlocks();
            $nvc = $c->getVersionToModify();
            $isApproved = $c->getVersionObject()->isApproved();
            foreach ($blocks as $b) {
                if ($b->getBlockTypeHandle() == 'content') {
                   //content block stuff
                }else{
                    $blockController = $b->getController();
                    $data = $blockController->getBlockControllerData();
                    //Get an array of the exported keys that could be internal collection ID's
                    $exportedKeys = $blockController->getBlockTypeExportPageColumns();
                    if (empty($exportedKeys)) {
                        continue;
                    }
                    $section = $target->getSection();
Juha replied on at Permalink Reply
Juha
You wrote that you have looked at ReplaceContentLinksTask. Was there something wrong with ReplaceBlockPageRelationsTask? It looks like it does pretty much what you need, the following being a crucial difference:

$columns = $db->MetaColumnNames($controller->getBlockTypeDatabaseTable());
$data = array();
$record = $controller->getBlockControllerData();
foreach ($columns as $key) {
    $data[$key] = $record->{$key};
}


Though I'm not sure how well this works with blocks that use multiple tables for storing data, like image slider, for example.
TheRealSean replied on at Permalink Reply
TheRealSean
The problem with ReplaceBlockPageRelationsTask is that it currently only works with content blocks.

We had a site that required most of the locale links to be updated manually.

I'm trying to look at a way to include more blocks
TheRealSean replied on at Permalink Reply
TheRealSean
Sorry Juha, my mistake
you are correct I should be in the ReplaceBlockPageRelationsTask (Don't know how I missed that)

looking at that I can see I was almost there :)

now I've just got to try and work out why that is not being run, when you rescan a site locale