Difference between Str::esc() and ->esc() / toUrl() and ->url()

When copying the image.php from the docs to customize some output I noticed that there are two ways of escaping and url declared:

if ($block->location() == 'web') {
    $src = $block->src()->esc();
} elseif ($image = $block->image()->toFile()) {
    $alt = $alt->or($image->alt());
    $src = $image->url();

<?php if ($src): ?>
<figure<?= Html::attr(['data-ratio' => $ratio, 'data-crop' => $crop], null, ' ') ?>>
  <?php if ($link->isNotEmpty()): ?>
  <a href="<?= Str::esc($link->toUrl()) ?>">
    <img src="<?= $src ?>" alt="<?= $alt->esc() ?>">

What is the difference of the two? Are they interchangable or is one always needed for lets say strings while the other for all other usecases?

In this example, esc() is a field method. A field method can only be called on a field object (and $block->src() returns a field).

$link->toUrl() returns a string. So you cannot call a field method, but have to fall back to Str::esc(), which expects a string as parameter.

Thanks for your explanation. Starting with Kirby I got a lot of debug errors dealing with strings or methods not applicable to stuff. I slowly get grips with the matter. Would you say this custom code for a CTA button is a clean way or do I have to include the Str::esc() as I already escaped on the field level? It doesn’t give a debug error…

$link = null;
$target = null;
if ($block->location() == 'web') {
  $link = $block->url()->esc();
  $target = 'rel="noopener noreferrer" target="_blank"';
} elseif ($page = $block->link()->toPage()) {
  $link = $page->url();

<a href="<?= $link ?>" <?= $target ?> class="btn" ?>>
  <?= $block->label()->isNotEmpty() ? $block->label() : t('readmore') ?>