Skip to content

Commit

Permalink
Merge pull request #7 from hyvor/metadata-priority
Browse files Browse the repository at this point in the history
metadata priority
  • Loading branch information
supun-io authored Oct 22, 2024
2 parents 397367e + 6146e48 commit c832c9e
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 92 deletions.
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

0 comments on commit c832c9e

Please sign in to comment.