Normalize the database #7975
Replies: 13 comments 9 replies
-
I think you're referring to the For the rest, I don't have a problem with what you're proposing per se, but it would be a pretty significant development effort. If it turns out that we're hitting e.g. a performance limit, then I could see prioritizing it -- but we'd need to establish that first. |
Beta Was this translation helpful? Give feedback.
-
Yeah, it was the |
Beta Was this translation helpful? Give feedback.
-
I like the idea but my big concern with normalizing the Because of this, normalizing the core localized fields might lead to more complex SQL queries, because we would then have to join across an additional table ( I think probably our biggest performance issue is around searching/filtering. I wonder if it makes sense -- where we identify a performance issue -- to create a normalized table specifically for searching/filtering. That could really inflate the size of our DB, though. |
Beta Was this translation helpful? Give feedback.
-
Yeah! I was updating a plugin and saw this feature being used =]
ps: I'll avoid discussing the real performance issues here, but I'm open for receiving pointers/data 😁 |
Beta Was this translation helpful? Give feedback.
-
Another issue related to our generic tables, is data corruption, on this screenshot from a user, it's possible to see a missing |
Beta Was this translation helpful? Give feedback.
-
About the settings tables, here are (in my opinion) our requirements/motivations:
Options for maintaining these features while moving to Eloquent models:
|
Beta Was this translation helpful? Give feedback.
-
I've looked at adding support for settings table in Eloquent. What I've tried so far:
class NewScope implements Scope
{
public function apply(Builder $query, Model $model)
{
$query->leftJoin(
table2,
table2.something_id,
'=',
table1.something_id
);
}
} This will initialise the model with attributes derived from columns of these two tables. The idea was to create a virtual columns where Schema::create('post_translations', function(Blueprint $table) {
$table->increments('id');
$table->integer('post_id')->unsigned();
$table->string('locale')->index();
$table->string('title');
$table->text('content');
$table->unique(['post_id', 'locale']);
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
}); That also will require additional code adaptations on our side but, at least, it already has an interface to interact with localized data. This method shows that they are initialising the new model for the translation table: https://github.com/Astrotomic/laravel-translatable/blob/main/src/Translatable/Translatable.php#L177 |
Beta Was this translation helpful? Give feedback.
-
After another look, I'd say that the best approach for preserving settings table is definitely creating a new Model and a custom Relation for it. That's a huge amount of work but the solution is definitely more extensible than dumping that into JSON. |
Beta Was this translation helpful? Give feedback.
-
Found 2 libraries that might be useful. They mimic settings table structure and create the way to interact with settings as attributes of the main Model. Both needed to be extended to support multilingual values: https://github.com/kodeine/laravel-meta/tree/2.0 and https://github.com/kolossal-io/laravel-multiplex/tree/main |
Beta Was this translation helpful? Give feedback.
-
Looking closer to those libraries I've decided that it would be easier to extend Eloquent than adapting those to fit our settings table model. What I'm suggesting is to override Eloquent Builder, which pairs the Model with the Query Builder.
|
Beta Was this translation helpful? Give feedback.
-
In general, we would need to keep some information from schema in the Model. I've created a property, which holds, for now, a setting name and boolean, whether it's multilingual. Settings not listed here, won't be processed. It's a place for potential plugin hook. protected array $settings = [
'familyName' => true,
'givenName' => true,
'affiliation' => true,
]; We might extend this to support data type, validation 'familyName' => [
'multilingual' => true,
'type' => 'string',
....
] Or specify type in The examples of what is already supported: // retrieves Model instance with settings as normal attributes
$user = UserModel::find(2);
// sets a multilingual setting value
$user->givenName = [
'en' => 'David'
];
// creates a new model and saves it in the database
$newUser = new UserModel();
$newUser->fill([
'email' => '[email protected]',
'givenName' => [
'en' => 'Skywaker',
'fr_CA' => 'Cielmarcheur'
],
'username' => 'lskywalker',
'password' => Str::random(10),
]);
// delete a model with associated settings
$newUser->delete(); What is missing so far, are filters for Models, which involve settings, e.g., search filter that we use in the User entity, something like: UserModel::whereSetting('givenName', 'LIKE', '...'); Also, interface to work with multilingual settings should be better. |
Beta Was this translation helpful? Give feedback.
-
I've added the ability for Models to execute select statements, like: UserModel::where('givenName', 'Waca')->first()
UserModel::whereIn('givenName', ['Daniel', 'Greg'])->get() Under the hood such calls are first delegated to the Eloquent Builder and then to the Query Builder. And in my scenario I'm just extending Eloquent Builder by adding a subquery to look for results in the setting table. I don't think we need more intervene in the Eloquent Builder logic (and I'm still considering whether overriding these 2 methods is necessary). Instead, additional filters/specifiers is better to handle on the Model's side as scopes by case per case basis. E.g., I'm looking currently at the complex case in |
Beta Was this translation helpful? Give feedback.
-
Keeping a note here that we would need to improve type casts for multilingual values. For now I'm omitting it as implementation might conflict with eloquence library, which we use for transforming Model's attributes to camel case |
Beta Was this translation helpful? Give feedback.
-
Describe the problem you would like to solve
Normalize the database in order to have better expectations (e.g. ensure a record is always identifiable by a single ID), improve the performance, decrease the amount of rows and also improve the data integrity.
Describe the solution you'd like
DONE: Add anID
field to all tables and discard composite primary keys. Change database tables schema for Clustering #3573Move non-localized fields from the
*_settings
tables to their origin table (e.g.user_settings
>user
), I think we have few cases, I just remember of seeing anissueId
somewhere. (filed for issueId)Move dynamic localized fields from the
*_settings
tables to a better typed tablesetting_name
for N rows)*_setting
table might be left (reserved for plugins) or removed altogether (plugins will deal with their data directly).Remove duplicated fields from dependent entities (e.g. remove the
review_round_files.stage_id
given thatstage_id
exists in its parent entityreview_rounds.stage_id
)Replace the columns assoc_type/assoc_id by "real" columns with integrity checks.
Who is asking for this feature?
Developer
Draft for the conversion process for the item 3
Taking the
user_settings
as an example, where each user have at least two rows in theuser_settings
(one for thegivenName
and another for thefamilyName
).user_locale
(id int, user_id int, locale varchar, given_name varchar, family_name varchar, ...)`, with all the localized fields (unique key index on the bolded fields), this way we'll have 1 record for each locale + user.Extra
If we're going to keep the
*_settings
for something else, perhaps we can use the JSON aggregate functions to return the data instead of having it loaded in a loop.Beta Was this translation helpful? Give feedback.
All reactions