Unique block ID

Permalink
Dear all,

I need a unique block ID to identify a block. I'll use this to set attributes to a block (saved in a seperate table). With this unique block ID I'll create URL's to this block (eg. domain/page#uniqeid).

When I use the block ID I have the problem that I have dead links in the table because the block ID chaned on every save of the page.

When I create a unique id from the first block ID I have the problem that when I use this bock with the clipboard i have two uniqe IDs.

Can somebody help me

Thanks in advance

gunter

GunterSchmitt
 
Gondwana replied on at Permalink Reply
Gondwana
I know this is old news, but in your block controller:
public function getBlockUID($b = null) {
        if ($b==null) return null;
        $proxyBlock = $b->getProxyBlock();
        return $proxyBlock? $proxyBlock->getBlockID() : $b->bID;
    }


In your view.php, you can then use:
$bUID = $controller->getBlockUID($b);
A3020 replied on at Permalink Reply
A3020
Nice solution. But why do you need to pass the $b?
Gondwana replied on at Permalink Reply
Gondwana
Because, as far as I can tell, the controller can't otherwise obtain this instance of the block. I tried using its 'getBlockObject', but the block thus obtained never has a proxyBlock.

If getBlockUID() were placed inside a descendant of the Block class rather than the Controller class, it would not then be necessary to pass $b. However, it just seemed more concretey to put the code in the controller; if nothing else, it avoids injecting multiple copies of it into any page that had multiple blocks of the same kind. I'm happy to stand corrected on this.

Methinks an ideal solution would be for getBlockUID() to be baked into Block.

A related topic:
https://www.concrete5.org/community/forums/customizing_c5/form-block...
JohntheFish replied on at Permalink Reply
JohntheFish
Yes, so we see too many instances of bID being used in the mistaken belief that it is unique. A block controller method to retrieve a unique block ID baked into the core would be a good enhancement. The mere presence of such a method would help encourage new developers to avoid mistakes.
Gondwana replied on at Permalink Reply
Gondwana
The convenience and visibility of $bID could still tilt things in its favour. An option would be to also introduce $bUID, but maintaining this in parallel with proxyBlock could be poor form unless the code is very well encapsulated. A safer (but fancier) option would be something like
public function __get($bUID) {
    return $this->getBlockUID();
}
mnakalay replied on at Permalink Reply
mnakalay
@John I don't understand. How can bID not be unique? It's an auto-generated primary key in the db, it has to be unique no? The core team has been professing the use of $bID to identify blocks in markup and inline styling. Am I missing something? Can you give an example of a situation where it wouldn't be unique?
Gondwana replied on at Permalink Reply
Gondwana
I know your question wasn't directed at me, but maybe I can give an example.

Plonk a Form block onto a page. Copy it to clipboard. Add a copy of the block to the same page via the clipboard. Save/publish. Inspect the page source. You'll probably find TWO instances of <div id="formblock123 ...">, where 123 is the bID of the Form block.

This only occurs if the two forms are basically just separate appearances (references?) of the one underlying instance. There is only one set of data to store, so one bID is sufficient for this. When c5 needs to distinguish between the two appearances of the form, I gather it uses proxyBlock. Each form appearance has a proxyBlock with a unique bID.

I think the same would be true of all block types. It's just easiest to see when using Form because it uses (misuses?) bID in the HTML it generates.
mnakalay replied on at Permalink Reply
mnakalay
Hey Peter, thank you for your answer. I had no idea. It feels very different than what was happening in legacy C5. But there, blocks would only account for 1 table and any added table had to be dealt with manually using the duplicate function.
JohntheFish replied on at Permalink Reply
JohntheFish
The unique bID problem has always existed in all versions of c5 sice copy/paste was introduced.

A block always has a unique bID in the database. However, if the same block is rendered more than once, each rendering will share that same bID.

Most of the time that is not important. However, when the bID is used for an html element #id attribute, a page can end up with duplicate element #ids, which is illegal html and gets picked upon by page analysers.

If JavaScript or CSS selects based on #id, which of the multiple elements identified is undefined. As far as I know browsers select the first occurrence because that is a safe optimisation.

If its merely a matter of valid HTML to satisfy search engines, the simple answer is to not generate unnecessary #id attributes.

When a unique #id is really needed in the html, there are several ways to generate it
- from the block instance (First posted by @mkly, refined as described above)
- using a random number or sequence
- from the c5 unique id helper (which uses random numbers internally)
- from other input in the block edit dialog (pass the problem back to the user)

Even with these measures, uniqueness can be broken by caching of block output.

Which to use is very much dependant on the application. For example, random generation is quick and simple but is not repeatable, block instance is repeatable, and user entered can be useful where disparate blocks need to communicate.

Better still, in many (but not all) cases, a little thought put into developing JavaScript that doesn't key on element #id can avoid the problem completely while often reducing download weight.
JohntheFish replied on at Permalink Reply
JohntheFish
PS. Hence a test I apply to any block in the PRB is to copy/paste it a few times in the same page.
A3020 replied on at Permalink Reply
A3020
I couldn't replicate this problem with the latest develop branch, am I missing something?

echo $bID.'<br />';
echo $controller->getBlockUID($b);


I tried the HTML block, copied it to the clipboard and then pasted it on a page. I see the same ID echoed twice. getProxyBlock seems to be false.

Would love the hear more about this.
Gondwana replied on at Permalink Reply
Gondwana
I would have thought you should have seen FOUR ID values (depending on where you placed that code). I'm probably misunderstanding.

Here's a way to observe the issue: put a php block on a page, with this:
<?php echo'bID='.$bID.'<br />';
echo 'bUID='.($b->getProxyBlock()? $b->getProxyBlock()->getBlockID() : $b->bID).'<br />';
echo 'controller identifier='.$controller->getIdentifier() .'<br />';
echo 'controller proxy? ' . ($controller->getBlockObject()->getProxyBlock()? 'yes' : 'no') .'<br />';
?>

Now copy the block and drop another instance of it on the same page.

You should see that both blocks report the same bID, but different bUIDs (because at least one of them has a proxyBlock). However, the controller is unaware of the proxyBlock in both cases (because getBlockObject only returns a 'generic' block).

This should show that bID shouldn't be used to uniquely identify anything on a page (such as a DIV id or form field name).

It also shows that controllers can't easily work out exactly which block they're controlling. But that's another story...
A3020 replied on at Permalink Reply
A3020
Aha, gotcha. I put that getBlockUID in the BlockController class. I can see the issue when putting it directly in a view.php file and using the $b instance.
GunterSchmitt replied on at Permalink Reply
GunterSchmitt
Hello Gondwana,

thank you. How can I compate the UbID with the bID in the controller.

When there is a difference I had a new block and then I can set all my attributes to this block (indepent from the version of this block).

I tried the followig code:
if ($this->getBlockUID($b) != $bID){
          $args['ubID'] = $this->getBlockUID($b);
       }


If I set the following code in the form-file I get always the same counts for both variables:

<div class="form-group row">
    <div class='col-xs-12'>
       <?php echo $form->text("bID", $bID, array (
         'autocomplete' => 'off',
         'readonly' => 'readonly',
      )); ?>
    </div>
</div>
<div class="form-group row">
    <div class='col-xs-12'>
       <?php echo $form->text("bID", $controller->getBlockUID($b), array (
         'autocomplete' => 'off',
         'readonly' => 'readonly',
      )); ?>
    </div>


But with the same code in a view-file I get different counts after I duplicated a block.

Thanks in advance

Gunter
Gondwana replied on at Permalink Reply
Gondwana
Glad you're still here, Gunter. :)

I can't find an easy/elegant way. Somehow, the block needs to 'back-channel' its bUID to the controller. I found a way here:
https://www.concrete5.org/community/forums/customizing_c5/form-block...
GunterSchmitt replied on at Permalink Reply 1 Attachment
GunterSchmitt
Hi Gondwana,

no I went crazy. Now nothing works. I always get the bID.

Can you be so kind and look at my attached sourcecode?

Thanks in advance
Gunter
Gondwana replied on at Permalink Reply
Gondwana
I've only got a couple of months' experience with c5, so I'm bound to get some things wrong.

What you're trying to do could be ambitious, because you'll need to keep track of every bID change that c5 makes to your block type. I think you'll want to implement duplicate() in your controller, probably also delete(), and maybe more. See
http://documentation.concrete5.org/developers/working-with-blocks/c...

I assume that the bID and bUID fields in form.php are diagnostics. The value(s) are assigned by c5 and mere mortals shouldn't have to deal with them.

I assume ubID is not used or implemented yet.

It seems that you've got the bUID detection working fine in view.php. I wrapped your output fields in a DIV with a unique id derived from bUID (see lines with <!-- gond --> appended). I think you should be able to link to those DIVs.

In your case, using bUID could be overkill. bUID will only differ from bID when there are identical copies of a block on a page. In that situation, it may not matter which block the link goes to, since the content would be the same. Perhaps you could get by with an identifier that is independent of c5's machinations, such as something derived from uniqid(). c5 won't go changing this on you. You may still need to pass your id from your form to your controller so it can store the value in your database table.

Note that Controller.save() isn't called when a block is copied and pasted. This denies you an opportunity to detect bUID at that time. As above, this may not matter. When a block is saved, it is given its own bID so you may not need to worry about bUID. You only need to keep track of the changing bID values (unless you use your own unique identifier).
<?php  defined("C5_EXECUTE") or die("Access Denied.");
$bUID = $controller->getBlockUID($b);?>
<div id="schmittstack<?php echo $bUID; ?>"> <!-- gond -->
   <div class="form-group row">
      <div class='col-xs-2'>bID:</div>
      <div class='col-xs-10'>
      <?php echo $form->text("bID", $bID, array (
            'autocomplete' => 'off',
            'readonly' => 'readonly',
      )); ?>
      </div>
   </div>
   <div class="form-group row">
      <div class='col-xs-2'>bUID:</div>
      <div class='col-xs-10'>
mnakalay replied on at Permalink Reply
mnakalay
Actually if memory serves, when a block is copied and pasted the controller function "duplicate" is called which contains the new $bID
public function duplicate($newBID){}

In the code it says "Automatically run when a block is duplicated. This most likely happens when a block is edited: a block is first duplicated, and then presented to the user to make changes." but if I remember well it is also called when copying and pasting.
Gondwana replied on at Permalink Reply
Gondwana
I did a quick test and didn't find that duplicate() was called when simply copying and pasting. It seems to only be called when a block gets its own unique bID, which happens when it's edited.

But I could have stuffed up my testing.
GunterSchmitt replied on at Permalink Reply
GunterSchmitt
I want to get a ID for all versions of the Block, so I can use this to create for example a mailing tool.

If I use the $bID a new mail will created after I edit the block. So my idear was to create a unique ID for each block independ of the version.