From 01f87fa57d05e9c9aa1706e94584e8a99f19d10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= <25438601+rafaucau@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:30:21 +0100 Subject: [PATCH 1/8] fix(multisite): update prefix after blog switch --- src/Orm/Database.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Orm/Database.php b/src/Orm/Database.php index bed03f3..d9a8d60 100644 --- a/src/Orm/Database.php +++ b/src/Orm/Database.php @@ -78,6 +78,11 @@ public function __construct() ); $this->db = $wpdb; + + // Add hook to update prefix when switching between blogs in multisite + add_action('switch_blog', function() { + $this->setTablePrefix($this->db->prefix); + }); } /** From 61cdffa525b1db45192edc8c679ba75ac1bcb4a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= <25438601+rafaucau@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:39:42 +0100 Subject: [PATCH 2/8] style(multisite): fix formatting --- src/Orm/Database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Orm/Database.php b/src/Orm/Database.php index d9a8d60..d7918b4 100644 --- a/src/Orm/Database.php +++ b/src/Orm/Database.php @@ -80,7 +80,7 @@ public function __construct() $this->db = $wpdb; // Add hook to update prefix when switching between blogs in multisite - add_action('switch_blog', function() { + add_action('switch_blog', function () { $this->setTablePrefix($this->db->prefix); }); } From 126e8f99f0e0d80c1ca486eac03718267e66853a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= <25438601+rafaucau@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:01:36 +0100 Subject: [PATCH 3/8] feat(multisite): shared tables --- src/Models/Meta/AbstractMeta.php | 5 +++++ src/Models/User.php | 5 +++++ src/Orm/AbstractModel.php | 15 +++++++++++++-- src/Orm/Database.php | 11 +++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/Models/Meta/AbstractMeta.php b/src/Models/Meta/AbstractMeta.php index be54f26..165e283 100644 --- a/src/Models/Meta/AbstractMeta.php +++ b/src/Models/Meta/AbstractMeta.php @@ -21,6 +21,11 @@ abstract class AbstractMeta extends AbstractModel */ public $timestamps = false; + /** + * @inheritdoc + */ + protected bool $useBasePrefix = true; + /** * @inheritDoc */ diff --git a/src/Models/User.php b/src/Models/User.php index 752995b..978d6b5 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -66,6 +66,11 @@ class User extends AbstractModel implements WithMetaModelInterface */ protected $table = 'users'; + /** + * @inheritdoc + */ + protected bool $useBasePrefix = true; + /** * @inheritDoc */ diff --git a/src/Orm/AbstractModel.php b/src/Orm/AbstractModel.php index adf0ca1..8ee6937 100644 --- a/src/Orm/AbstractModel.php +++ b/src/Orm/AbstractModel.php @@ -22,6 +22,12 @@ abstract class AbstractModel extends Model */ protected $guarded = []; + /** + * Indicates if the model should use base prefix for multisite shared tables. + * @var bool + */ + protected bool $useBasePrefix = false; + /** * @param array $attributes */ @@ -34,9 +40,14 @@ public function __construct(array $attributes = []) /** * @inheritDoc */ - public function getTable() + public function getTable(): ?string { - $prefix = $this->getConnection()->getTablePrefix(); + /** @var Database $connection */ + $connection = $this->getConnection(); + $prefix = $this->useBasePrefix + ? $connection->getBaseTablePrefix() + : $connection->getTablePrefix(); + if ($this->table !== null && $this->table !== '') { return str_starts_with($this->table, $prefix) ? $this->table : $prefix . $this->table; } diff --git a/src/Orm/Database.php b/src/Orm/Database.php index d7918b4..b49e117 100644 --- a/src/Orm/Database.php +++ b/src/Orm/Database.php @@ -447,6 +447,17 @@ public function logQuery($query, $bindings, $time = null): void */ } + /** + * Get the base table prefix for multisite installation. + * This prefix is shared across all sites in the network. + * + * @return string Base prefix for multisite shared tables + */ + public function getBaseTablePrefix(): string + { + return $this->db->base_prefix; + } + /** * @inheritDoc */ From baa10770b93eb8d61b3ebe4f310d50f04bd93ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= <25438601+rafaucau@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:04:55 +0100 Subject: [PATCH 4/8] fix(multisite): move useBasePrefix from AbstractMeta to UserMeta model --- src/Models/Meta/AbstractMeta.php | 5 ----- src/Models/Meta/UserMeta.php | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Models/Meta/AbstractMeta.php b/src/Models/Meta/AbstractMeta.php index 165e283..be54f26 100644 --- a/src/Models/Meta/AbstractMeta.php +++ b/src/Models/Meta/AbstractMeta.php @@ -21,11 +21,6 @@ abstract class AbstractMeta extends AbstractModel */ public $timestamps = false; - /** - * @inheritdoc - */ - protected bool $useBasePrefix = true; - /** * @inheritDoc */ diff --git a/src/Models/Meta/UserMeta.php b/src/Models/Meta/UserMeta.php index 377e77e..cb33c98 100644 --- a/src/Models/Meta/UserMeta.php +++ b/src/Models/Meta/UserMeta.php @@ -29,6 +29,11 @@ class UserMeta extends AbstractMeta */ protected $table = 'usermeta'; + /** + * @inheritdoc + */ + protected bool $useBasePrefix = true; + /** * @return HasOne */ From 6201895e65bfc7a02c9cd4ae399b7591c0522d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= <25438601+rafaucau@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:14:26 +0100 Subject: [PATCH 5/8] feat(multisite): add Multisite models https://codex.wordpress.org/Database_Description#Multisite_Table_Details --- src/Models/Network/Blog.php | 78 ++++++++++++++++++++++++++ src/Models/Network/BlogVersion.php | 44 +++++++++++++++ src/Models/Network/RegistrationLog.php | 49 ++++++++++++++++ src/Models/Network/Signup.php | 63 +++++++++++++++++++++ src/Models/Network/Site.php | 50 +++++++++++++++++ src/Models/Network/SiteMeta.php | 37 ++++++++++++ 6 files changed, 321 insertions(+) create mode 100644 src/Models/Network/Blog.php create mode 100644 src/Models/Network/BlogVersion.php create mode 100644 src/Models/Network/RegistrationLog.php create mode 100644 src/Models/Network/Signup.php create mode 100644 src/Models/Network/Site.php create mode 100644 src/Models/Network/SiteMeta.php diff --git a/src/Models/Network/Blog.php b/src/Models/Network/Blog.php new file mode 100644 index 0000000..50f6113 --- /dev/null +++ b/src/Models/Network/Blog.php @@ -0,0 +1,78 @@ + + */ + +namespace Dbout\WpOrm\Models\Network; + +use Carbon\Carbon; +use Dbout\WpOrm\Orm\AbstractModel; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasOne; + +/** + * @property-read int $blog_id + * @property int $site_id + * @property string $domain + * @property string $path + * @property Carbon $registered + * @property Carbon $last_updated + * @property bool $public + * @property bool $archived + * @property bool $mature + * @property bool $spam + * @property bool $deleted + * @property int $lang_id + * + * @property-read Site $site + * @property-read BlogVersion|null $version + */ +class Blog extends AbstractModel +{ + public const CREATED_AT = self::REGISTERED; + public const UPDATED_AT = self::LAST_UPDATED; + final public const BLOG_ID = 'blog_id'; + final public const SITE_ID = 'site_id'; + final public const DOMAIN = 'domain'; + final public const PATH = 'path'; + final public const REGISTERED = 'registered'; + final public const LAST_UPDATED = 'last_updated'; + final public const PUBLIC = 'public'; + final public const ARCHIVED = 'archived'; + final public const MATURE = 'mature'; + final public const SPAM = 'spam'; + final public const DELETED = 'deleted'; + final public const LANG_ID = 'lang_id'; + + protected $primaryKey = self::BLOG_ID; + + protected bool $useBasePrefix = true; + + protected $casts = [ + self::BLOG_ID => 'int', + self::SITE_ID => 'int', + self::REGISTERED => 'datetime', + self::LAST_UPDATED => 'datetime', + self::PUBLIC => 'bool', + self::ARCHIVED => 'bool', + self::MATURE => 'bool', + self::SPAM => 'bool', + self::DELETED => 'bool', + self::LANG_ID => 'int', + ]; + + protected $table = 'blogs'; + + public function site(): BelongsTo + { + return $this->belongsTo(Site::class, self::SITE_ID); + } + + public function version(): HasOne + { + return $this->hasOne(BlogVersion::class, BlogVersion::BLOG_ID); + } +} diff --git a/src/Models/Network/BlogVersion.php b/src/Models/Network/BlogVersion.php new file mode 100644 index 0000000..76fea99 --- /dev/null +++ b/src/Models/Network/BlogVersion.php @@ -0,0 +1,44 @@ + + */ + +namespace Dbout\WpOrm\Models\Network; + +use Carbon\Carbon; +use Dbout\WpOrm\Orm\AbstractModel; +use Illuminate\Database\Eloquent\Relations\BelongsTo; + +/** + * @property-read int $blog_id + * @property string $db_version + * @property Carbon $last_updated + * + * @property-read Blog $blog + */ +class BlogVersion extends AbstractModel +{ + public const CREATED_AT = null; + public const UPDATED_AT = self::LAST_UPDATED; + final public const BLOG_ID = 'blog_id'; + final public const DB_VERSION = 'db_version'; + final public const LAST_UPDATED = 'last_updated'; + + protected bool $useBasePrefix = true; + + protected $table = 'blog_versions'; + + protected $primaryKey = self::BLOG_ID; + + protected $casts = [ + self::LAST_UPDATED => 'datetime', + ]; + + public function blog(): BelongsTo + { + return $this->belongsTo(Blog::class, self::BLOG_ID); + } +} diff --git a/src/Models/Network/RegistrationLog.php b/src/Models/Network/RegistrationLog.php new file mode 100644 index 0000000..a362283 --- /dev/null +++ b/src/Models/Network/RegistrationLog.php @@ -0,0 +1,49 @@ + + */ + +namespace Dbout\WpOrm\Models\Network; + +use Carbon\Carbon; +use Dbout\WpOrm\Orm\AbstractModel; +use Illuminate\Database\Eloquent\Relations\BelongsTo; + +/** + * @property-read int $ID + * @property string $email + * @property string $IP + * @property int $blog_id + * @property Carbon $date_registered + * + * @property-read Blog|null $blog + */ +class RegistrationLog extends AbstractModel +{ + public const CREATED_AT = self::DATE_REGISTERED; + public const UPDATED_AT = null; + final public const ID = 'ID'; + final public const EMAIL = 'email'; + final public const IP = 'IP'; + final public const BLOG_ID = 'blog_id'; + final public const DATE_REGISTERED = 'date_registered'; + + protected bool $useBasePrefix = true; + + protected $table = 'registration_log'; + + protected $primaryKey = self::ID; + + protected $casts = [ + self::BLOG_ID => 'int', + self::DATE_REGISTERED => 'datetime', + ]; + + public function blog(): BelongsTo + { + return $this->belongsTo(Blog::class, self::BLOG_ID); + } +} diff --git a/src/Models/Network/Signup.php b/src/Models/Network/Signup.php new file mode 100644 index 0000000..285d0f0 --- /dev/null +++ b/src/Models/Network/Signup.php @@ -0,0 +1,63 @@ + + */ + +namespace Dbout\WpOrm\Models\Network; + +use Carbon\Carbon; +use Dbout\WpOrm\Models\User; +use Dbout\WpOrm\Orm\AbstractModel; +use Illuminate\Database\Eloquent\Relations\HasOne; + +/** + * @property-read int $signup_id + * @property string $domain + * @property string $path + * @property string $title + * @property string $user_login + * @property string $user_email + * @property Carbon $registered + * @property Carbon $activated + * @property bool $active + * @property string $activation_key + * @property string|null $meta + * + * @property-read User|null $user + */ +class Signup extends AbstractModel +{ + public const CREATED_AT = self::REGISTERED; + public const UPDATED_AT = null; + final public const SIGNUP_ID = 'signup_id'; + final public const DOMAIN = 'domain'; + final public const PATH = 'path'; + final public const TITLE = 'title'; + final public const USER_LOGIN = 'user_login'; + final public const USER_EMAIL = 'user_email'; + final public const REGISTERED = 'registered'; + final public const ACTIVATED = 'activated'; + final public const ACTIVE = 'active'; + final public const ACTIVATION_KEY = 'activation_key'; + final public const META = 'meta'; + + protected bool $useBasePrefix = true; + + protected $table = 'signups'; + + protected $primaryKey = self::SIGNUP_ID; + + protected $casts = [ + self::REGISTERED => 'datetime', + self::ACTIVATED => 'datetime', + self::ACTIVE => 'bool', + ]; + + public function user(): HasOne + { + return $this->hasOne(User::class, User::EMAIL, self::USER_EMAIL); + } +} diff --git a/src/Models/Network/Site.php b/src/Models/Network/Site.php new file mode 100644 index 0000000..308b6b0 --- /dev/null +++ b/src/Models/Network/Site.php @@ -0,0 +1,50 @@ + + */ + +namespace Dbout\WpOrm\Models\Network; + +use Dbout\WpOrm\Concerns\HasMetas; +use Dbout\WpOrm\MetaMappingConfig; +use Dbout\WpOrm\Orm\AbstractModel; +use Illuminate\Database\Eloquent\Collection; +use Illuminate\Database\Eloquent\Relations\HasMany; + +/** + * @property-read int $id + * @property string $domain + * @property string $path + * + * @property-read Collection $metas + * @property-read Collection $blogs + */ +class Site extends AbstractModel +{ + use HasMetas; + + public const CREATED_AT = null; + public const UPDATED_AT = null; + final public const ID = 'id'; + final public const DOMAIN = 'domain'; + final public const PATH = 'path'; + + protected bool $useBasePrefix = true; + + protected $table = 'site'; + + protected $primaryKey = self::ID; + + public function blogs(): HasMany + { + return $this->hasMany(Blog::class, Blog::SITE_ID); + } + + public function getMetaConfigMapping(): MetaMappingConfig + { + return new MetaMappingConfig(SiteMeta::class, SiteMeta::SITE_ID); + } +} diff --git a/src/Models/Network/SiteMeta.php b/src/Models/Network/SiteMeta.php new file mode 100644 index 0000000..e6585f7 --- /dev/null +++ b/src/Models/Network/SiteMeta.php @@ -0,0 +1,37 @@ + + */ + +namespace Dbout\WpOrm\Models\Network; + +use Dbout\WpOrm\Models\Meta\AbstractMeta; +use Illuminate\Database\Eloquent\Relations\HasOne; + +/** + * @property-read int $meta_id + * @property int $site_id + * @property string|null $meta_key + * @property mixed|null $meta_value + * + * @property-read Site $site + */ +class SiteMeta extends AbstractMeta +{ + final public const META_ID = 'meta_id'; + final public const SITE_ID = 'site_id'; + + protected bool $useBasePrefix = true; + + protected $table = 'sitemeta'; + + protected $primaryKey = self::META_ID; + + public function site(): HasOne + { + return $this->hasOne(Site::class, Site::ID, self::SITE_ID); + } +} From b64076e6e843618cb21ce7665ce87f32b1a1771a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= <25438601+rafaucau@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:17:52 +0100 Subject: [PATCH 6/8] fix(multisite): reset Database instance on blog switch --- src/Orm/Database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Orm/Database.php b/src/Orm/Database.php index b49e117..0e04af7 100644 --- a/src/Orm/Database.php +++ b/src/Orm/Database.php @@ -81,7 +81,7 @@ public function __construct() // Add hook to update prefix when switching between blogs in multisite add_action('switch_blog', function () { - $this->setTablePrefix($this->db->prefix); + self::$instance = null; }); } From 7337a789f027a57d66848d847e15860cf1fdcef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= <25438601+rafaucau@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:22:30 +0100 Subject: [PATCH 7/8] refactor(multisite): add addWordPresHooks method --- src/Orm/Database.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Orm/Database.php b/src/Orm/Database.php index 0e04af7..f68d60e 100644 --- a/src/Orm/Database.php +++ b/src/Orm/Database.php @@ -78,8 +78,12 @@ public function __construct() ); $this->db = $wpdb; + $this->addWordPresHooks(); + } - // Add hook to update prefix when switching between blogs in multisite + protected function addWordPresHooks(): void + { + // Reset Database instance when switching between blogs in multisite to update prefix add_action('switch_blog', function () { self::$instance = null; }); From 37be6a3a2260e509e120076de9728604f74eb899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ca=C5=82ka?= <25438601+rafaucau@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:31:52 +0100 Subject: [PATCH 8/8] refactor(multisite): change namespace of multisite models --- src/Models/{Network => Multisite}/Blog.php | 2 +- src/Models/{Network => Multisite}/BlogVersion.php | 2 +- src/Models/{Network => Multisite}/RegistrationLog.php | 2 +- src/Models/{Network => Multisite}/Signup.php | 2 +- src/Models/{Network => Multisite}/Site.php | 2 +- src/Models/{Network => Multisite}/SiteMeta.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename src/Models/{Network => Multisite}/Blog.php (98%) rename src/Models/{Network => Multisite}/BlogVersion.php (96%) rename src/Models/{Network => Multisite}/RegistrationLog.php (96%) rename src/Models/{Network => Multisite}/Signup.php (97%) rename src/Models/{Network => Multisite}/Site.php (96%) rename src/Models/{Network => Multisite}/SiteMeta.php (95%) diff --git a/src/Models/Network/Blog.php b/src/Models/Multisite/Blog.php similarity index 98% rename from src/Models/Network/Blog.php rename to src/Models/Multisite/Blog.php index 50f6113..b24fa82 100644 --- a/src/Models/Network/Blog.php +++ b/src/Models/Multisite/Blog.php @@ -6,7 +6,7 @@ * Author: Dimitri BOUTEILLE */ -namespace Dbout\WpOrm\Models\Network; +namespace Dbout\WpOrm\Models\Multisite; use Carbon\Carbon; use Dbout\WpOrm\Orm\AbstractModel; diff --git a/src/Models/Network/BlogVersion.php b/src/Models/Multisite/BlogVersion.php similarity index 96% rename from src/Models/Network/BlogVersion.php rename to src/Models/Multisite/BlogVersion.php index 76fea99..521ed13 100644 --- a/src/Models/Network/BlogVersion.php +++ b/src/Models/Multisite/BlogVersion.php @@ -6,7 +6,7 @@ * Author: Dimitri BOUTEILLE */ -namespace Dbout\WpOrm\Models\Network; +namespace Dbout\WpOrm\Models\Multisite; use Carbon\Carbon; use Dbout\WpOrm\Orm\AbstractModel; diff --git a/src/Models/Network/RegistrationLog.php b/src/Models/Multisite/RegistrationLog.php similarity index 96% rename from src/Models/Network/RegistrationLog.php rename to src/Models/Multisite/RegistrationLog.php index a362283..4c6e986 100644 --- a/src/Models/Network/RegistrationLog.php +++ b/src/Models/Multisite/RegistrationLog.php @@ -6,7 +6,7 @@ * Author: Dimitri BOUTEILLE */ -namespace Dbout\WpOrm\Models\Network; +namespace Dbout\WpOrm\Models\Multisite; use Carbon\Carbon; use Dbout\WpOrm\Orm\AbstractModel; diff --git a/src/Models/Network/Signup.php b/src/Models/Multisite/Signup.php similarity index 97% rename from src/Models/Network/Signup.php rename to src/Models/Multisite/Signup.php index 285d0f0..2b72611 100644 --- a/src/Models/Network/Signup.php +++ b/src/Models/Multisite/Signup.php @@ -6,7 +6,7 @@ * Author: Dimitri BOUTEILLE */ -namespace Dbout\WpOrm\Models\Network; +namespace Dbout\WpOrm\Models\Multisite; use Carbon\Carbon; use Dbout\WpOrm\Models\User; diff --git a/src/Models/Network/Site.php b/src/Models/Multisite/Site.php similarity index 96% rename from src/Models/Network/Site.php rename to src/Models/Multisite/Site.php index 308b6b0..b48843c 100644 --- a/src/Models/Network/Site.php +++ b/src/Models/Multisite/Site.php @@ -6,7 +6,7 @@ * Author: Dimitri BOUTEILLE */ -namespace Dbout\WpOrm\Models\Network; +namespace Dbout\WpOrm\Models\Multisite; use Dbout\WpOrm\Concerns\HasMetas; use Dbout\WpOrm\MetaMappingConfig; diff --git a/src/Models/Network/SiteMeta.php b/src/Models/Multisite/SiteMeta.php similarity index 95% rename from src/Models/Network/SiteMeta.php rename to src/Models/Multisite/SiteMeta.php index e6585f7..d2d49bf 100644 --- a/src/Models/Network/SiteMeta.php +++ b/src/Models/Multisite/SiteMeta.php @@ -6,7 +6,7 @@ * Author: Dimitri BOUTEILLE */ -namespace Dbout\WpOrm\Models\Network; +namespace Dbout\WpOrm\Models\Multisite; use Dbout\WpOrm\Models\Meta\AbstractMeta; use Illuminate\Database\Eloquent\Relations\HasOne;