From 9444c34071c124306b2f5a5d6386da5c605323ed Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Tue, 2 Nov 2021 10:42:21 +0000 Subject: [PATCH] [ENTITY][Actor][ActorLanguage][Language] Remove Actor::preferred_lang_id. Add ActorLanguage::order. Add Language::{short_display,long_display}. Instead of an actor having a single preffered language, the entries in ActorLanguage should be used, sorted by ActorLanguage::order --- src/Entity/Actor.php | 84 ++++++++++++++++-------------------- src/Entity/ActorLanguage.php | 17 +++++++- src/Entity/Language.php | 48 ++++++++++++++++----- 3 files changed, 89 insertions(+), 60 deletions(-) diff --git a/src/Entity/Actor.php b/src/Entity/Actor.php index ce9d50461d..27fb8934bc 100644 --- a/src/Entity/Actor.php +++ b/src/Entity/Actor.php @@ -56,7 +56,7 @@ class Actor extends Entity private int $id; private string $nickname; private ?string $fullname = null; - private int $roles = 4; + private int $roles = 4; private ?string $homepage; private ?string $bio; private ?string $location; @@ -64,7 +64,6 @@ class Actor extends Entity private ?float $lon; private ?int $location_id; private ?int $location_service; - private ?int $preferred_lang_id; private DateTimeInterface $created; private DateTimeInterface $modified; @@ -98,7 +97,7 @@ class Actor extends Entity public function getFullname(): ?string { - if (is_null($this->fullname)) { + if (\is_null($this->fullname)) { return null; } return $this->fullname; @@ -192,17 +191,6 @@ class Actor extends Entity return $this->location_service; } - public function setPreferredLangId(?string $preferred_lang_id): self - { - $this->preferred_lang_id = $preferred_lang_id; - return $this; - } - - public function getPreferredLangId(): ?int - { - return $this->preferred_lang_id; - } - public function setCreated(DateTimeInterface $created): self { $this->created = $created; @@ -324,17 +312,18 @@ class Actor extends Entity { // Will throw exception on invalid input. $nickname = Nickname::normalize($nickname, check_already_used: false); - return Cache::get('relative-nickname-' . $nickname . '-' . $this->getId(), - fn () => DB::dql(<< $nickname, 'actor_id' => $this->getId()], - ['limit' => 1] - )[0] ?? null + return Cache::get( + 'relative-nickname-' . $nickname . '-' . $this->getId(), + fn () => DB::dql( + <<<'EOF' + select a from actor a where + a.id in (select fa.followed from follow fa join actor aa with fa.followed = aa.id where fa.follower = :actor_id and aa.nickname = :nickname) or + a.id in (select fb.follower from follow fb join actor ab with fb.follower = ab.id where fb.followed = :actor_id and ab.nickname = :nickname) or + a.nickname = :nickname + EOF, + ['nickname' => $nickname, 'actor_id' => $this->getId()], + ['limit' => 1], + )[0] ?? null, ); } @@ -367,16 +356,18 @@ EOF * Get the most appropraite language for $this to use when * referring to $context (a reply or a group, for instance) * - * @return string the Language as a string (save space in cache) + * @return string[] the Languages as string (save space in cache) */ - public function getPreferredLanguageChoice(?self $context = null): string + public function getPreferredLanguageChoices(?self $context = null): array { - $lang_id = $context?->getPreferredLangId() ?? $this->getPreferredLangId(); - if (\is_null($lang_id)) { - return Common::config('site', 'language'); - } - $key = 'actor-lang-' . $this->getId() . (!\is_null($context) ? '-' . $context->getId() : ''); - return Cache::get($key, fn () => (string) DB::findOneBy('language', ['id' => $lang_id])); + $id = $context?->getId() ?? $this->getId(); + return Cache::get( + 'actor-' . $this->getId() . '-langs' . (!\is_null($context) ? '-' . $context->getId() : ''), + fn () => DB::dql( + 'select l from actor_language al join language l with al.language_id = l.id where al.actor_id = :id order by al.order ASC', + ['id' => $id], + ) ?: [DB::findOneBy('language', ['locale' => Common::config('site', 'language')])], + ); } public static function schemaDef(): array @@ -385,20 +376,19 @@ EOF 'name' => 'actor', 'description' => 'local and remote users, groups and bots are actors, for instance', 'fields' => [ - 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'], - 'nickname' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username'], - 'fullname' => ['type' => 'text', 'description' => 'display name'], - 'roles' => ['type' => 'int', 'not null' => true, 'default' => UserRoles::USER, 'description' => 'Bitmap of permissions this actor has'], - 'homepage' => ['type' => 'text', 'description' => 'identifying URL'], - 'bio' => ['type' => 'text', 'description' => 'descriptive biography'], - 'location' => ['type' => 'text', 'description' => 'physical location'], - 'lat' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'], - 'lon' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'], - 'location_id' => ['type' => 'int', 'description' => 'location id if possible'], - 'location_service' => ['type' => 'int', 'description' => 'service used to obtain location id'], - 'preferred_lang_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Language.id', 'multiplicity' => 'one to many', 'description' => 'preferred language'], - 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], - 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], + 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'], + 'nickname' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username'], + 'fullname' => ['type' => 'text', 'description' => 'display name'], + 'roles' => ['type' => 'int', 'not null' => true, 'default' => UserRoles::USER, 'description' => 'Bitmap of permissions this actor has'], + 'homepage' => ['type' => 'text', 'description' => 'identifying URL'], + 'bio' => ['type' => 'text', 'description' => 'descriptive biography'], + 'location' => ['type' => 'text', 'description' => 'physical location'], + 'lat' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'], + 'lon' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'], + 'location_id' => ['type' => 'int', 'description' => 'location id if possible'], + 'location_service' => ['type' => 'int', 'description' => 'service used to obtain location id'], + 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], + 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], ], 'primary key' => ['id'], 'indexes' => [ diff --git a/src/Entity/ActorLanguage.php b/src/Entity/ActorLanguage.php index 7490db4cec..1a3b13b53b 100644 --- a/src/Entity/ActorLanguage.php +++ b/src/Entity/ActorLanguage.php @@ -42,6 +42,7 @@ class ActorLanguage extends Entity // @codeCoverageIgnoreStart private int $actor_id; private int $language_id; + private int $order; public function setActorId(int $actor_id): self { @@ -64,6 +65,17 @@ class ActorLanguage extends Entity { return $this->language_id; } + + public function getOrder(): int + { + return $this->order; + } + + public function setOrder(int $order): self + { + $this->order = $order; + return $this; + } // @codeCoverageIgnoreEnd // }}} Autocode @@ -73,8 +85,9 @@ class ActorLanguage extends Entity 'name' => 'actor_language', 'description' => 'join table where one actor can have many languages', 'fields' => [ - 'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'the actor this language entry refers to'], - 'language_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Language.id', 'multiplicity' => 'many to many', 'not null' => true, 'description' => 'the language this entry refers to'], + 'actor_id' => ['type' => 'int', 'not null' => true, 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to many', 'not null' => true, 'description' => 'the actor this language entry refers to'], + 'language_id' => ['type' => 'int', 'not null' => true, 'foreign key' => true, 'target' => 'Language.id', 'multiplicity' => 'many to many', 'not null' => true, 'description' => 'the language this entry refers to'], + 'order' => ['type' => 'int', 'not null' => true, 'description' => 'the order in which a user\'s language options should be displayed'], ], 'primary key' => ['actor_id', 'language_id'], 'indexes' => [ diff --git a/src/Entity/Language.php b/src/Entity/Language.php index a4e228ecd6..7d31e0d654 100644 --- a/src/Entity/Language.php +++ b/src/Entity/Language.php @@ -41,7 +41,9 @@ class Language extends Entity // {{{ Autocode // @codeCoverageIgnoreStart private int $id; - private string $language; + private string $locale; + private string $long_display; + private string $short_display; private DateTimeInterface $created; public function setId(int $id): self @@ -55,15 +57,37 @@ class Language extends Entity return $this->id; } - public function setLanguage(string $language): self + public function setLocale(string $locale): self { - $this->language = $language; + $this->locale = $locale; return $this; } - public function getLanguage(): string + public function getLocale(): string { - return $this->language; + return $this->locale; + } + + public function setLongDisplay(string $long_display): self + { + $this->long_display = $long_display; + return $this; + } + + public function getLongDisplay(): string + { + return $this->long_display; + } + + public function setShortDisplay(string $short_display): self + { + $this->short_display = $short_display; + return $this; + } + + public function getShortDisplay(): string + { + return $this->short_display; } public function setCreated(DateTimeInterface $created): self @@ -81,7 +105,7 @@ class Language extends Entity public function __toString() { - return $this->getLanguage(); + return $this->getLongDisplay(); } public static function schemaDef(): array @@ -90,16 +114,18 @@ class Language extends Entity 'name' => 'language', 'description' => 'all known languages', 'fields' => [ - 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'], - 'language' => ['type' => 'char', 'length' => 64, 'description' => 'The locale identifier for the language of a note. 2-leter-iso-language-code_4-leter-script-code_2-leter-iso-country-code, but kept longer in case we get a different format'], - 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], + 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'], + 'locale' => ['type' => 'char', 'length' => 64, 'description' => 'The locale identifier for the language of a note. 2-leter-iso-language-code_4-leter-script-code_2-leter-iso-country-code, but kept longer in case we get a different format'], + 'long_display' => ['type' => 'varchar', 'length' => 64, 'description' => 'The long display string for the language, in english (translated later)'], + 'short_display' => ['type' => 'varchar', 'length' => 12, 'description' => 'The short display string for the language (used for the first option)'], + 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], ], 'primary key' => ['id'], 'unique keys' => [ - 'language_language_uniq' => ['language'], + 'language_locale_uniq' => ['locale'], ], 'indexes' => [ - 'language_idx' => ['language'], + 'locale_idx' => ['locale'], ], ]; }