# Adding a method \`get\_like\` to PluginStore class

**URL:** https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026
**Category:** Development
**Created:** [August 6, 2019, 6:12pm UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026 "2019-08-06T18:12:26Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![fzngagan](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/fzngagan/32/259349_2.png) [@fzngagan](https://meta.discourse.org/u/fzngagan)
#### Post date: [August 6, 2019, 6:12pm UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026/1 "2019-08-06T18:12:26Z")

</div>

I want to submit a PR to add a method `get_like` to the PluginStore.

get\_like will fetch all the records for a given plugin, whose key starts with the same word.

**Usecase**  
If a plugin has two or more entities they want to store say apples and oranges, they’ll store the data as apple\_1, apple\_2.. and orange\_1, orange\_2 from a plugin names fruits.

To fetch all the `oranges`, one can simply call PluginStore.get\_like(‘fruits’, ‘orange’) etc.

What does the team and plugin devs think about this?

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [August 6, 2019, 6:37pm UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026/2 "2019-08-06T18:37:56Z")

</div>

I don’t think I’ve personally needed this pattern. Have you seen it in public plugins, or just the one you are creating?

Also it’s not too long to type:

`PluginStoreRow.where("plugin_name = 'fruits' AND key LIKE 'orange%')`

I would recommend adding a helper function or class to your plugin if you do this often. Also be sure to make sure you have appropriate indexes for these queries.

---

<div class="post-metadata">

### Author: ![fzngagan](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/fzngagan/32/259349_2.png) [@fzngagan](https://meta.discourse.org/u/fzngagan)
#### Post date: [August 6, 2019, 7:04pm UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026/3 "2019-08-06T19:04:02Z")

</div>

I was working on this feature for the custom-wizards plugin and I wanted to fetch all the wizards.  
The key used is a string which is unique per wizard. This case was handled by fetching all the rows for the plugin and filtering them based on a for the `value` which is fine for small number of records but not ideal for a big plugin. It looks like plugin\_store\_rows is meant to store `settings-like` data for the plugin.

I personally think, this would open the idea of storing `non-settings` like data in the plugin\_store\_rows table.

(Forgive me if it sounds strange)

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [August 6, 2019, 7:15pm UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026/4 "2019-08-06T19:15:56Z")

</div>

In general I recommend creating your own tables via migrations if the `PluginStoreRow` can’t be queried the way you want to. This is now commonly done in several plugins and works great!

---

<div class="post-metadata">

### Author: ![fzngagan](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/fzngagan/32/259349_2.png) [@fzngagan](https://meta.discourse.org/u/fzngagan)
#### Post date: [August 7, 2019, 1:46am UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026/5 "2019-08-07T01:46:00Z")

</div>

Ohh. I wasn’t aware of this. Thanks a lot.

---

<div class="post-metadata">

### Author: ![angus](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/angus/32/341715_2.png) [@angus](https://meta.discourse.org/u/angus)
#### Post date: [August 7, 2019, 11:51pm UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026/6 "2019-08-07T23:51:57Z")

</div>

> [@fzngagan](#):
>
> If a plugin has two or more entities they want to store say apples and oranges, they’ll store the data as apple\_1, apple\_2… and orange\_1, orange\_2 from a plugin names fruits.

The pattern is to use the `key` field in the `plugin_store_rows` table to store both a namespace and a unique identifier, i.e.:

```plaintext
<namespace>_<id>

```

This pattern is seen less in core Discourse plugins these days with a general decline in the use of PluginStore for example:

- OAuth2 Basic Plugin [used it to store associated account data](https://github.com/discourse/discourse-oauth2-basic/commit/5ae9f35e814cdc160dcd4ad77d613a70c76a5228#diff-b61ae67bc6a57a3decb8d15b832077f4L167) until we migrated it to user\_associated\_accounts.

- Poll plugin [used it prior to the migration to it’s own table](https://github.com/discourse/discourse/blob/master/plugins/poll/lib/tasks/migrate_old_polls.rake#L32).

However it is still used in places, including the core Discourse codebase itself, e.g. [in the Reviewables model](https://github.com/discourse/discourse/blob/master/app/models/reviewable.rb#L187).

I also use the pattern in a number of plugins.

The main reason the pattern is used is because the plugin\_store\_rows table is used by multiple plugins (and some core services), so the identifying columns, i.e. `id` and `plugin_name`, can’t be used for identification internally within each system using the PluginStore. So a string-based system is used in the `key` column instead.

> [@eviltrout](#):
>
> In general I recommend creating your own tables via migrations if the `PluginStoreRow` can’t be queried the way you want to. This is now commonly done in several plugins and works great!

In terms of changing the database structure from within a plugin, @gdpelican has a good post on this:

> [@New columns in directory (/users)](https://meta.discourse.org/t/new-columns-in-directory-users/77814/5):
>
> FWIW I’ve decided to go this route with a plugin of mine, where PluginStoreRow and CustomFields didn’t qqquite cut it on their own. warning Caveat emptor warning You should be trying really hard not to do this - It’s not recommended behaviour to modify the database from within a plugin, but it can be done unobtrusively in small, additive doses (I’d say you never for any reason want to remove or change existing columns) Here’s how I did it: Write a migration in the plugin which modifies th…

Personally, I’m still quite wary of doing this as working on a third party plugin you have no control over namespacing, whether your plugin is removed and what potentially conflicting changes are made to core Discourse.

As @gdpelican mentions, you need to provide a way for your plugin user to remove the db changes if they uninstall the plugin.

> Provide a method for users to clean up your database changes if they don’t want your plugin anymore. I did it with a rake task.

I feel this is too in the weeds for most plugin users and poses a risk if they’re not aware of this detail.

Moreover, I haven’t found a real need to go outside the bounds of the PluginStore and CustomFields yet.

All that said, personally I’d be in favour of a new method along these lines in PluginStore, as I find the pattern useful.

@david Would be interested in your thoughts on the above as well.

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [August 8, 2019, 9:09am UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026/7 "2019-08-08T09:09:36Z")

</div>

As @eviltrout has said, we’re using migrations and dedicated tables in a number of plugins now, with great success. Having the ability to enforce database constraints has helped improve performance (lookups in any column) and data integrity (through unique indexes). These two things have proved especially important at the scale of some of our hosted customers - not really something I considered before joining the team.

The first substantial plugin I worked on was chat-integration, and I implemented a very fiddly “fake activerecord”, which leans on the plugin store. In hindsight, dedicated tables would have been a far better choice, and I might look at migrating the plugin to that in future.

> [@angus](#):
>
> I’m still quite wary of doing this

I would agree, when it comes to **modifying** core tables. Adding/modifying columns on existing tables could have unintended consequences later down the line, and will stick around even if the plugin is uninstalled. I would strongly recommend against doing this.

Dedicated tables, on the other hand, are fairly low risk. If the plugin is uninstalled, they will just stick around, without any negative side effects (as long as you don’t introduce any foreign key constraints). Leaving data lying around is “no worse” than using the plugin store.

In terms of cleanup, we could look at providing rake tasks which “reverse” plugin migrations to cleanup. But to be honest, I don’t think this will see much use. My assumption is that people rarely uninstall plugins, and when they do, they would rather keep the data around in case they want to reinstall it again.

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [August 8, 2019, 3:30pm UTC](https://meta.discourse.org/t/adding-a-method-get-like-to-pluginstore-class/125026/8 "2019-08-08T15:30:36Z")

</div>

> [@angus](#):
>
> However it is still used in places, including the core Discourse codebase itself, e.g. [in the Reviewables model](https://github.com/discourse/discourse/blob/master/app/models/reviewable.rb#L187).

As the author of that code I should maybe clarify it a little bit. The priority ids are constants and not relational data. I would absolutely recommend against using `something_id` when the ids are not known in advance. In this case each priority is thought of as a singleton, and I figured that any table I’d create would essentially be a clone of the PluginStore anyway!
