Refactor models to use new attribute cast

This commit is contained in:
Jonny Barnes 2022-11-26 10:50:19 +00:00
parent a8de52077f
commit cfca6a1de5
Signed by: jonny
SSH key fingerprint: SHA256:CTuSlns5U7qlD9jqHvtnVmfYV3Zwl2Z7WnJ4/dqOaL8
9 changed files with 218 additions and 292 deletions

View file

@ -75,7 +75,7 @@ class MicropubMediaController extends Controller
return [
'url' => $mediaItem->url,
'published' => $mediaItem->created_at->toW3cString(),
'mime_type' => $mediaItem->getMimeType(),
'mime_type' => $mediaItem->mimetype,
];
});

View file

@ -6,6 +6,7 @@ namespace App\Models;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -58,13 +59,10 @@ class Article extends Model
*/
protected $guarded = ['id'];
/**
* Process the article for display.
*
* @return string
*/
public function getHtmlAttribute(): string
protected function html(): Attribute
{
return Attribute::get(
get: function () {
$environment = new Environment();
$environment->addExtension(new CommonMarkCoreExtension());
$environment->addRenderer(FencedCode::class, new FencedCodeRenderer());
@ -72,56 +70,43 @@ class Article extends Model
$markdownConverter = new MarkdownConverter($environment);
return $markdownConverter->convert($this->main)->getContent();
},
);
}
/**
* Convert updated_at to W3C time format.
*
* @return string
*/
public function getW3cTimeAttribute(): string
protected function w3cTime(): Attribute
{
return $this->updated_at->toW3CString();
return Attribute::get(
get: fn () => $this->updated_at->toW3CString(),
);
}
/**
* Convert updated_at to a tooltip appropriate format.
*
* @return string
*/
public function getTooltipTimeAttribute(): string
protected function tooltipTime(): Attribute
{
return $this->updated_at->toRFC850String();
return Attribute::get(
get: fn () => $this->updated_at->toRFC850String(),
);
}
/**
* Convert updated_at to a human readable format.
*
* @return string
*/
public function getHumanTimeAttribute(): string
protected function humanTime(): Attribute
{
return $this->updated_at->diffForHumans();
return Attribute::get(
get: fn () => $this->updated_at->diffForHumans(),
);
}
/**
* Get the pubdate value for RSS feeds.
*
* @return string
*/
public function getPubdateAttribute(): string
protected function pubdate(): Attribute
{
return $this->updated_at->toRSSString();
return Attribute::get(
get: fn () => $this->updated_at->toRSSString(),
);
}
/**
* A link to the article, i.e. `/blog/1999/12/25/merry-christmas`.
*
* @return string
*/
public function getLinkAttribute(): string
protected function link(): Attribute
{
return '/blog/' . $this->updated_at->year . '/' . $this->updated_at->format('m') . '/' . $this->titleurl;
return Attribute::get(
get: fn () => '/blog/' . $this->updated_at->year . '/' . $this->updated_at->format('m') . '/' . $this->titleurl,
);
}
/**
@ -134,7 +119,7 @@ class Article extends Model
*/
public function scopeDate(Builder $query, int $year = null, int $month = null): Builder
{
if ($year == null) {
if ($year === null) {
return $query;
}
$start = $year . '-01-01 00:00:00';

View file

@ -4,43 +4,11 @@ declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Carbon;
/**
* App\Models\Bookmark.
*
* @property int $id
* @property string $url
* @property string|null $name
* @property string|null $content
* @property string|null $screenshot
* @property string|null $archive
* @property array|null $syndicates
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
* @property-read string $longurl
* @property-read Collection|Tag[] $tags
* @property-read int|null $tags_count
*
* @method static Builder|Bookmark newModelQuery()
* @method static Builder|Bookmark newQuery()
* @method static Builder|Bookmark query()
* @method static Builder|Bookmark whereArchive($value)
* @method static Builder|Bookmark whereContent($value)
* @method static Builder|Bookmark whereCreatedAt($value)
* @method static Builder|Bookmark whereId($value)
* @method static Builder|Bookmark whereName($value)
* @method static Builder|Bookmark whereScreenshot($value)
* @method static Builder|Bookmark whereSyndicates($value)
* @method static Builder|Bookmark whereUpdatedAt($value)
* @method static Builder|Bookmark whereUrl($value)
* @mixin Eloquent
*/
class Bookmark extends Model
{
use HasFactory;
@ -71,13 +39,10 @@ class Bookmark extends Model
return $this->belongsToMany('App\Models\Tag');
}
/**
* The full url of a bookmark.
*
* @return string
*/
public function getLongurlAttribute(): string
protected function longurl(): Attribute
{
return config('app.url') . '/bookmarks/' . $this->id;
return Attribute::get(
get: fn () => config('app.url') . '/bookmarks/' . $this->id,
);
}
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Models;
use App\Traits\FilterHtml;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
@ -17,39 +18,29 @@ class Like extends Model
protected $fillable = ['url'];
/**
* Normalize the URL of a Like.
*
* @param string $value The provided URL
*/
public function setUrlAttribute(string $value)
protected function url(): Attribute
{
$this->attributes['url'] = normalize_url($value);
return Attribute::set(
set: fn ($value) => normalize_url($value),
);
}
/**
* Normalize the URL of the author of the like.
*
* @param string|null $value The authors url
*/
public function setAuthorUrlAttribute(?string $value)
protected function authorUrl(): Attribute
{
$this->attributes['author_url'] = normalize_url($value);
return Attribute::set(
set: fn ($value) => normalize_url($value),
);
}
/**
* If the content contains HTML, filter it.
*
* @param string|null $value The content of the like
* @return string|null
*/
public function getContentAttribute(?string $value): ?string
protected function content(): Attribute
{
return Attribute::get(
get: function ($value, $attributes) {
if ($value === null) {
return null;
}
$mf2 = Mf2\parse($value, $this->url);
$mf2 = Mf2\parse($value, $attributes['url']);
if (Arr::get($mf2, 'items.0.properties.content.0.html')) {
return $this->filterHtml(
@ -59,4 +50,6 @@ class Like extends Model
return $value;
}
);
}
}

View file

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -37,87 +38,38 @@ class Media extends Model
return $this->belongsTo(Note::class);
}
/**
* Get the URL for an S3 media file.
*
* @return string
*/
public function getUrlAttribute(): string
protected function url(): Attribute
{
if (Str::startsWith($this->path, 'https://')) {
return $this->path;
return Attribute::get(
get: function ($value, $attributes) {
if (Str::startsWith($attributes['path'], 'https://')) {
return $attributes['path'];
}
return config('filesystems.disks.s3.url') . '/' . $this->path;
return config('filesystems.disks.s3.url') . '/' . $attributes['path'];
}
);
}
/**
* Get the URL for the medium size of an S3 image file.
*
* @return string
*/
public function getMediumurlAttribute(): string
protected function mediumurl(): Attribute
{
$basename = $this->getBasename($this->path);
$extension = $this->getExtension($this->path);
return config('filesystems.disks.s3.url') . '/' . $basename . '-medium.' . $extension;
return Attribute::get(
get: fn ($value, $attributes) => $this->getSizeUrl($attributes['path'], 'medium'),
);
}
/**
* Get the URL for the small size of an S3 image file.
*
* @return string
*/
public function getSmallurlAttribute(): string
protected function smallmurl(): Attribute
{
$basename = $this->getBasename($this->path);
$extension = $this->getExtension($this->path);
return config('filesystems.disks.s3.url') . '/' . $basename . '-small.' . $extension;
return Attribute::get(
get: fn ($value, $attributes) => $this->getSizeUrl($attributes['path'], 'small'),
);
}
/**
* Give the real part of a filename, i.e. strip the file extension.
*
* @param string $path
* @return string
*/
public function getBasename(string $path): string
protected function mimetype(): Attribute
{
// the following achieves this data flow
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
$filenameParts = explode('.', $path);
array_pop($filenameParts);
return ltrim(array_reduce($filenameParts, static function ($carry, $item) {
return $carry . '.' . $item;
}, ''), '.');
}
/**
* Get the extension from a given filename.
*
* @param string $path
* @return string
*/
public function getExtension(string $path): string
{
$parts = explode('.', $path);
return array_pop($parts);
}
/**
* Get the mime type of the media file.
*
* For now we will just use the extension, but this could be improved.
*
* @return string
*/
public function getMimeType(): string
{
$extension = $this->getExtension($this->path);
return Attribute::get(
get: function ($value, $attributes) {
$extension = $this->getExtension($attributes['path']);
return match ($extension) {
'gif' => 'image/gif',
@ -130,5 +82,34 @@ class Media extends Model
'mkv' => 'video/mkv',
default => 'application/octet-stream',
};
},
);
}
private function getSizeUrl(string $path, string $size): string
{
$basename = $this->getBasename($path);
$extension = $this->getExtension($path);
return config('filesystems.disks.s3.url') . '/' . $basename . '-' . $size . '.' . $extension;
}
private function getBasename(string $path): string
{
// the following achieves this data flow
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
$filenameParts = explode('.', $path);
array_pop($filenameParts);
return ltrim(array_reduce($filenameParts, static function ($carry, $item) {
return $carry . '.' . $item;
}, ''), '.');
}
private function getExtension(string $path): string
{
$parts = explode('.', $path);
return array_pop($parts);
}
}

View file

@ -6,6 +6,7 @@ namespace App\Models;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
@ -105,53 +106,46 @@ class Place extends Model
]));
}
/**
* The Long URL for a place.
*
* @return string
*/
public function getLongurlAttribute(): string
protected function longurl(): Attribute
{
return config('app.url') . '/places/' . $this->slug;
return Attribute::get(
get: fn ($value, $attributes) => config('app.url') . '/places/' . $attributes['slug'],
);
}
/**
* The Short URL for a place.
*
* @return string
*/
public function getShorturlAttribute(): string
protected function shorturl(): Attribute
{
return config('app.shorturl') . '/places/' . $this->slug;
return Attribute::get(
get: fn ($value, $attributes) => config('app.shorturl') . '/places/' . $attributes['slug'],
);
}
/**
* This method is an alternative for `longurl`.
*
* @return string
*/
public function getUriAttribute(): string
protected function uri(): Attribute
{
return $this->longurl;
return Attribute::get(
get: fn () => $this->longurl,
);
}
/**
* Dealing with a jsonb column, so we check input first.
*
* @param string|null $url
*/
public function setExternalUrlsAttribute(?string $url)
protected function externalUrls(): Attribute
{
if ($url === null) {
return;
return Attribute::set(
set: function ($value, $attributes) {
if ($value === null) {
return $attributes['external_urls'] ?? null;
}
$type = $this->getType($url);
$type = $this->getType($value);
$already = [];
if (array_key_exists('external_urls', $this->attributes)) {
$already = json_decode($this->attributes['external_urls'], true);
if (array_key_exists('external_urls', $attributes)) {
$already = json_decode($attributes['external_urls'], true);
}
$already[$type] = $url;
$this->attributes['external_urls'] = json_encode($already);
$already[$type] = $value;
return json_encode($already);
}
);
}
/**

View file

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
@ -40,14 +41,11 @@ class Tag extends Model
return $this->belongsToMany('App\Models\Bookmark');
}
/**
* When creating a Tag model instance, invoke the nomralize method on the tag.
*
* @param string $value
*/
public function setTagAttribute(string $value)
protected function tag(): Attribute
{
$this->attributes['tag'] = $this->normalize($value);
return Attribute::set(
set: fn ($value) => self::normalize($value),
);
}
/**

View file

@ -6,13 +6,14 @@ namespace App\Models;
use App\Traits\FilterHtml;
use Codebird\Codebird;
use Exception;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Cache;
use Jonnybarnes\WebmentionsParser\Authorship;
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
class WebMention extends Model
{
@ -43,20 +44,22 @@ class WebMention extends Model
return $this->morphTo();
}
/**
* Get the author of the webmention.
*
* @return array
*
* @throws AuthorshipParserException
*/
public function getAuthorAttribute(): array
protected function author(): Attribute
{
return Attribute::get(
get: function ($value, $attributes) {
if (
! array_key_exists('mf2', $attributes) ||
$attributes['mf2'] === null
) {
return null;
}
$authorship = new Authorship();
$hCard = $authorship->findAuthor(json_decode($this->mf2, true));
$hCard = $authorship->findAuthor(json_decode($attributes['mf2'], true));
if ($hCard === false) {
return [];
return null;
}
if (
@ -68,22 +71,21 @@ class WebMention extends Model
return $hCard;
}
);
}
/**
* Get the published value for the webmention.
*
* @return string|null
*/
public function getPublishedAttribute(): ?string
protected function published(): Attribute
{
$mf2 = $this->mf2 ?? '';
return Attribute::get(
get: function ($value, $attributes) {
$mf2 = $attributes['mf2'] ?? '';
$microformats = json_decode($mf2, true);
if (isset($microformats['items'][0]['properties']['published'][0])) {
try {
$published = carbon()->parse(
$microformats['items'][0]['properties']['published'][0]
)->toDayDateTimeString();
} catch (\Exception $exception) {
} catch (Exception) {
$published = $this->updated_at->toDayDateTimeString();
}
} else {
@ -92,24 +94,30 @@ class WebMention extends Model
return $published;
}
);
}
/**
* Get the filtered HTML of a reply.
*
* @return string|null
*/
public function getReplyAttribute(): ?string
protected function reply(): Attribute
{
if ($this->mf2 === null) {
return Attribute::get(
get: function ($value, $attributes) {
if (
! array_key_exists('mf2', $attributes) ||
$attributes['mf2'] === null
) {
return null;
}
$microformats = json_decode($this->mf2, true);
$microformats = json_decode($attributes['mf2'], true);
if (isset($microformats['items'][0]['properties']['content'][0]['html'])) {
return $this->filterHtml($microformats['items'][0]['properties']['content'][0]['html']);
}
return null;
}
);
}
/**
* Create the photo link.
@ -121,11 +129,13 @@ class WebMention extends Model
{
$url = normalize_url($url);
$host = parse_url($url, PHP_URL_HOST);
if ($host == 'pbs.twimg.com') {
if ($host === 'pbs.twimg.com') {
//make sure we use HTTPS, we know twitter supports it
return str_replace('http://', 'https://', $url);
}
if ($host == 'twitter.com') {
if ($host === 'twitter.com') {
if (Cache::has($url)) {
return Cache::get($url);
}
@ -137,6 +147,7 @@ class WebMention extends Model
return $profile_image;
}
$filesystem = new Filesystem();
if ($filesystem->exists(public_path() . '/assets/profile-images/' . $host . '/image')) {
return '/assets/profile-images/' . $host . '/image';

View file

@ -83,7 +83,6 @@ class PlacesTest extends TestCase
'url' => ['https://www.openstreetmap.org/way/1234'],
],
]);
$this->assertInstanceOf('App\Models\Place', $ret);
$this->assertCount(11, Place::all());
}