Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
New LinkData methods
  • Loading branch information
brandonkelly committed Jun 26, 2024
commit 1161723a07f9421a2fe5cbb1c65ab74f8f288827
72 changes: 66 additions & 6 deletions src/fields/data/LinkData.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@

namespace craft\fields\data;

use craft\base\ElementInterface;
use craft\base\Serializable;
use craft\fields\linktypes\BaseElementLinkType;
use craft\fields\linktypes\BaseLinkType;
use craft\helpers\Html;
use craft\helpers\Template;
use Twig\Markup;
use yii\base\BaseObject;

/**
* Link field data class.
*
* @property-read string $type The link type ID
* @property-read string $value The link value
* @property-read string $label The link label
* @property-read Markup|null $link An anchor tag for this link
* @property-read ElementInterface|null $element The element linked by the field, if there is one
* @author Pixel & Tonic, Inc. <[email protected]>
* @since 5.3.0
*/
Expand All @@ -37,19 +46,70 @@ public function __construct(
}

public function __toString(): string
{
return $this->getValue();
}

/**
* Returns the link type ID.
*
* @return string
*/
public function getType(): string
{
return $this->type::id();
}

/**
* Returns the link value.
*/
public function getValue(): string
{
if (!isset($this->renderedValue)) {
/** @var BaseLinkType|string $type */
/** @phpstan-var class-string<BaseLinkType> $type */
$type = $this->type;
$this->renderedValue = $type::render($this->value);
$this->renderedValue = $this->type::render($this->value);
}
return $this->renderedValue;
}

public function getType(): string
/**
* Returns the link label.
*
* @return string
*/
public function getLabel(): string
{
return $this->type::id();
return $this->type::linkLabel($this->value);
}

/**
* Returns an anchor tag for this link.
*
* @return Markup|null
*/
public function getLink(): ?Markup
{
$url = $this->getValue();
if ($url === '') {
$html = '';
} else {
$label = $this->getLabel();
$html = Html::a(Html::encode($label !== '' ? $label : $url), $url);
}

return Template::raw($html);
}

/**
* Returns the element linked by the field, if there is one.
*
* @return ElementInterface|null
*/
public function getElement(): ?ElementInterface
{
if (!is_subclass_of($this->type, BaseElementLinkType::class)) {
return null;
}
return $this->type::element($this->value);
}

public function serialize(): mixed
Expand Down
75 changes: 48 additions & 27 deletions src/fields/linktypes/BaseElementLinkType.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
*/
abstract class BaseElementLinkType extends BaseLinkType
{
/**
* @var array<string,ElementInterface|false>
* @see element()
*/
private static array $fetchedElements = [];

/**
* Returns the element type this link type is for.
*
Expand All @@ -47,37 +53,18 @@ public static function supports(string $value): bool

public static function render(string $value): string
{
return Craft::$app->getElements()->parseRefs($value);
return self::element($value)?->getUrl() ?? '';
}

public static function inputHtml(Link $field, ?string $value, string $containerId): string
public static function linkLabel(string $value): string
{
$elements = [];

if ($value && preg_match(sprintf('/^\{%s:(\d+)(?:@(\d+))?:url\}$/', static::elementType()::refHandle()), $value, $match)) {
$id = $match[1];
$siteId = $match[2] ?? null;
$query = static::elementType()::find()
->id((int)$id)
->status(null)
->drafts(null)
->revisions(null);

if ($siteId) {
$query->siteId((int)$siteId);
} else {
$query
->site('*')
->unique()
->preferSites([Craft::$app->getSites()->getCurrentSite()->id]);
}

$element = $query->one();
if ($element) {
$elements[] = $element;
}
}
$element = self::element($value);
return $element ? (string)$element : '';
}

public static function inputHtml(Link $field, ?string $value, string $containerId): string
{
$elements = array_filter([self::element($value)]);
$id = sprintf('elementselect%s', mt_rand());

$view = Craft::$app->getView();
Expand Down Expand Up @@ -141,4 +128,38 @@ public static function validate(string $value, ?string &$error = null): bool
{
return true;
}

public static function element(?string $value): ?ElementInterface
{
if (
!$value ||
!preg_match(sprintf('/^\{%s:(\d+)(?:@(\d+))?:url\}$/', static::elementType()::refHandle()), $value, $match)
) {
return null;
}

if (!isset(self::$fetchedElements[$value])) {
$id = $match[1];
$siteId = $match[2] ?? null;

$query = static::elementType()::find()
->id((int)$id)
->status(null)
->drafts(null)
->revisions(null);

if ($siteId) {
$query->siteId((int)$siteId);
} else {
$query
->site('*')
->unique()
->preferSites([Craft::$app->getSites()->getCurrentSite()->id]);
}

self::$fetchedElements[$value] = $query->one() ?? false;
}

return self::$fetchedElements[$value] ?: null;
}
}
7 changes: 7 additions & 0 deletions src/fields/linktypes/BaseLinkType.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ public static function render(string $value): string
return $value;
}

/**
* Returns the default link label for [[\craft\fields\data\LinkData::getLabel()]].
*
* @return string
*/
abstract public static function linkLabel(string $value): string;

/**
* Returns the input HTML that should be shown when this link type is selected.
*
Expand Down
13 changes: 9 additions & 4 deletions src/fields/linktypes/BaseTextLinkType.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ public static function normalize(string $value): string
return static::validate($normalized) ? $normalized : $value;
}

public static function linkLabel(string $value): string
{
foreach ((array)static::urlPrefix() as $prefix) {
$value = StringHelper::removeLeft($value, $prefix);
}
return $value;
}

public static function inputHtml(Link $field, ?string $value, string $containerId): string
{
$name = 'value';
Expand Down Expand Up @@ -80,10 +88,7 @@ public static function inputHtml(Link $field, ?string $value, string $containerI
]);

if ($value && static::validate($value)) {
$linkText = $value;
foreach ((array)static::urlPrefix() as $prefix) {
$linkText = StringHelper::removeLeft($linkText, $prefix);
}
$linkText = self::linkLabel($value);
$html =
Html::beginTag('div', [
'class' => ['chip', 'small'],
Expand Down