Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

metadata priority #7

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 9 additions & 4 deletions src/Link/Metadata/MetadataObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

namespace Hyvor\Unfold\Link\Metadata;

use DateTimeInterface;
use Hyvor\Unfold\Unfolded\UnfoldedAuthor;
use Hyvor\Unfold\Unfolded\UnfoldedTag;

/**
* @template T extends string|DateTimeInterface|UnfoldedAuthor|UnfoldedTag
*/
class MetadataObject
{

/**
* @param MetadataKeyType $key
* @param T $value
*/
public function __construct(
public MetadataKeyType $key,
public string|DateTimeInterface|UnfoldedAuthor|UnfoldedTag $value,
public $value,
) {
}
}
193 changes: 193 additions & 0 deletions src/Link/Metadata/MetadataPriority.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?php

namespace Hyvor\Unfold\Link\Metadata;

use Hyvor\Unfold\Unfolded\UnfoldedAuthor;
use DateTimeInterface;
use Hyvor\Unfold\Unfolded\UnfoldedTag;

/**
* This class select the best metadata to use in the Unfolded object
*/
class MetadataPriority
{

/**
* @param MetadataObject[] $metadata
*/
public function __construct(private array $metadata)
{
}

/**
* @param MetadataKeyType[] $keys
*/
private function prioritizedAll(array $keys)
{
$keyedMetadata = [];

/**
* First, we group the metadata by their key and filters metadata we don't want
* Each key may have multiple metadata
*
* [
* 'OG_TITLE' => [MetadataObject],
* 'TITLE' => [MetadataObject, MetadataObject],
* ]
*/
foreach ($this->metadata as $metadata) {
$key = $metadata->key;
if (!in_array($key, $keys)) {
continue;
}

$keyName = $key->name;
if (isset($keyedMetadata[$keyName])) {
$keyedMetadata[$keyName][] = $metadata;
} else {
$keyedMetadata[$keyName] = [$metadata];
}
}

/**
* Next, we are sorting $keyedMetadata by the order of $keys
* If $keys was [TITLE, OG_TITLE], then the order of $keyedMetadata will be
* [
* 'TITLE' => [MetadataObject, MetadataObject],
* 'OG_TITLE' => [MetadataObject],
* ]
*/
$keysNames = array_map(fn($key) => $key->name, $keys);
uksort($keyedMetadata, function ($a, $b) use ($keysNames) {
return array_search($a, $keysNames) - array_search($b, $keysNames);
});

// index by 0,1,2
$keyedMetadata = array_values($keyedMetadata);
// return the values
return array_map(fn($metadata) => $metadata->value, $keyedMetadata[0] ?? []);
}

/**
* @param MetadataKeyType[] $keys
* @return mixed|null
*/
private function prioritized(array $keys)
{
return $this->prioritizedAll($keys)[0] ?? null;
}

public function title(): ?string
{
return $this->prioritized(
[
MetadataKeyType::TITLE,
MetadataKeyType::OG_TITLE,
MetadataKeyType::TWITTER_TITLE
]
);
}

public function description(): ?string
{
return $this->prioritized([
MetadataKeyType::DESCRIPTION,
MetadataKeyType::OG_DESCRIPTION,
MetadataKeyType::TWITTER_DESCRIPTION
]);
}

/**
* @return UnfoldedAuthor[]
*/
public function authors()
{
return $this->prioritizedAll([
MetadataKeyType::RICH_SCHEMA_AUTHOR,
MetadataKeyType::OG_ARTICLE_AUTHOR,
MetadataKeyType::TWITTER_CREATOR
]);
}

/**
* @return UnfoldedTag[]
*/
public function tags(): array
{
return $this->prioritizedAll([
MetadataKeyType::OG_ARTICLE_TAG
]);
}


public function siteName(): ?string
{
return $this->prioritized([
MetadataKeyType::OG_SITE_NAME,
MetadataKeyType::TWITTER_SITE
]);
}


public function siteUrl(): ?string
{
return $this->prioritized([
MetadataKeyType::OG_URL
]);
}


public function canonicalUrl(): ?string
{
return $this->prioritized([
MetadataKeyType::CANONICAL_URL
]);
}


public function publishedTime(): ?DateTimeInterface
{
return $this->prioritized([
MetadataKeyType::RICH_SCHEMA_PUBLISHED_TIME,
MetadataKeyType::OG_ARTICLE_PUBLISHED_TIME
]);
}


public function modifiedTime(): ?DateTimeInterface
{
return $this->prioritized([
MetadataKeyType::RICH_SCHEMA_MODIFIED_TIME,
MetadataKeyType::OG_ARTICLE_MODIFIED_TIME
]);
}


public function thumbnailUrl(): ?string
{
return $this->prioritized([
MetadataKeyType::OG_IMAGE,
MetadataKeyType::OG_IMAGE_URL,
MetadataKeyType::OG_IMAGE_SECURE_URL,
MetadataKeyType::TWITTER_IMAGE
]);
}


public function iconUrl(): ?string
{
return $this->prioritized([
MetadataKeyType::FAVICON_URL
]);
}


public function locale(): ?string
{
return $this->prioritized([
MetadataKeyType::LOCALE,
MetadataKeyType::OG_LOCALE
]);
}

}
37 changes: 20 additions & 17 deletions src/Unfolded/Unfolded.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Hyvor\Unfold\Embed\EmbedResponseObject;
use Hyvor\Unfold\Link\Metadata\MetadataKeyType;
use Hyvor\Unfold\Link\Metadata\MetadataObject;
use Hyvor\Unfold\Link\Metadata\MetadataPriority;
use Hyvor\Unfold\UnfoldCallContext;
use Hyvor\Unfold\UnfoldMethod;

Expand Down Expand Up @@ -203,9 +204,9 @@ public static function getMetadataFromKeys(
$value[] = $meta->value;
$keyIndex = array_search($meta->key, $keys); // set the new key index
} elseif ($isMultiple && ($keyIndex === array_search(
$meta->key,
$keys
))) { // if multiple values are allowed and same priority key found
$meta->key,
$keys
))) { // if multiple values are allowed and same priority key found
$value[] = $meta->value;
}
}
Expand All @@ -216,7 +217,7 @@ public static function getMetadataFromKeys(
return $isMultiple ?
$value : // return the array of values
(
count($value) !== 0 ?
count($value) !== 0 ?
$value[0] : // return the first value
null // return null if no value found
);
Expand All @@ -229,23 +230,25 @@ public static function fromMetadata(
string $url,
array $metadata,
UnfoldCallContext $context,
) {
): self {
$metadataPriority = new MetadataPriority($metadata);

return new self(
$context->method,
$url,
null,
self::title($metadata),
self::description($metadata),
self::authors($metadata),
self::tags($metadata),
self::siteName($metadata),
self::siteUrl($metadata),
self::canonicalUrl($metadata),
self::publishedTime($metadata),
self::modifiedTime($metadata),
self::thumbnailUrl($metadata),
self::iconUrl($metadata),
self::locale($metadata),
$metadataPriority->title(),
$metadataPriority->description(),
$metadataPriority->authors(),
$metadataPriority->tags(),
$metadataPriority->siteName(),
$metadataPriority->siteUrl(),
$metadataPriority->canonicalUrl(),
$metadataPriority->publishedTime(),
$metadataPriority->modifiedTime(),
$metadataPriority->thumbnailUrl(),
$metadataPriority->iconUrl(),
$metadataPriority->locale(),
$context->duration()
);
}
Expand Down
12 changes: 4 additions & 8 deletions tests/Feature/UnfoldTest.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
<?php

it('gets link', function () {
//
use Hyvor\Unfold\Unfold;

});

it('gets embed', function () {
//

});
it('fetches link', function () {
$response = Unfold::unfold('https://hyvor.com');
});
49 changes: 49 additions & 0 deletions tests/Unit/Link/Metadata/MetadataPriorityTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Hyvor\Unfold\Tests\Unit;

use Hyvor\Unfold\Link\Metadata\MetadataKeyType;
use Hyvor\Unfold\Link\Metadata\MetadataObject;
use Hyvor\Unfold\Link\Metadata\MetadataPriority;

it('prioritizes title', function () {
$metadata = [
new MetadataObject(MetadataKeyType::DESCRIPTION, 'description'), // ignored
new MetadataObject(MetadataKeyType::OG_TITLE, 'og title'),
new MetadataObject(MetadataKeyType::TITLE, 'title'),
];

$priority = new MetadataPriority($metadata);
$title = $priority->title();
expect($title)->toBe('title');
});

it('prioritized authors', function () {
$metadata = [
new MetadataObject(MetadataKeyType::OG_ARTICLE_AUTHOR, 'Author1'),
new MetadataObject(MetadataKeyType::OG_ARTICLE_AUTHOR, 'Author2'),
new MetadataObject(MetadataKeyType::OG_ARTICLE_AUTHOR, 'Author3'),
// some tags
new MetadataObject(MetadataKeyType::OG_ARTICLE_TAG, 'Tag1'),
new MetadataObject(MetadataKeyType::OG_ARTICLE_TAG, 'Tag2'),
];

$priority = new MetadataPriority($metadata);
$authors = $priority->authors();
expect($authors)->toEqual([
'Author1',
'Author2',
'Author3',
]);
});

it('with already correct sort', function () {
$metadata = [
new MetadataObject(MetadataKeyType::OG_SITE_NAME, 'OG Site name'),
new MetadataObject(MetadataKeyType::TWITTER_SITE, 'Twitter Site Name'),
];

$priority = new MetadataPriority($metadata);
$authors = $priority->siteName();
expect($authors)->toEqual('OG Site name');
});
18 changes: 0 additions & 18 deletions tests/Unit/RedirectTest.php

This file was deleted.

Loading
Loading