Package::saveConfig is gone :/

Permalink 2 users found helpful
Hi,

since the Package::saveConfig is now gone, any guidelines on how we should be using the Config object (or should be be directly using the ConfigStore object?)

goutnet
View Replies: View Best Answer
Korvin replied on at Permalink Best Answer Reply
Korvin
For configuration values in 5.7, we now have what we call "ConfigRepository"'s that have a standard interface for reading and writing to a store. We have set two of these up, one for reading and writing to the filesystem "config" and one for reading and writing to the database "config/database".

Both of these are instances of the same Repository class with different "Config\LoaderInterface" and "Config\SaverInterface" implementations injected.

The api for these looks like this:
$database_config = \Core::make('config/database');
// Get the value of `namespace::group.item` defaulting to "default_value"
$value = $database_config->get('namespace::group.key', 'default_value');
// Set the value of `namespace::group.item` in runtime
$database_config->set('namespace::group.key');
// Save the value of `namespace::group.item` to the store.
$database_config->save('namespace::group.key');


In order to use either one of these with your package, we've created a "Repository\Liaison" class that allows you to default the namespace for the get|set|save|has methods.
Package objects handle setting themselves up with this system, so you just have to do $pkg->getConfig() for the database config liaison and $pkg->getFileConfig() for the filesystem config liaison.

For example, if you'd like to add a toggleable option named "show_header" to your package and store the value in the database, your configuration "item" should be "show_header", your "group" should be something semantic like "front_end" and your namespace should be your package handle.

So for a package with the handle "my_package" it'd be something like this:
// Get the value defaulting to false
$show_header = $package->getConfig()->get('front_end.show_header', false);
// Conditionally toggle it
if ($disable_header === true && $show_header) {
    $package->getConfig()->save('front_end.show_header', false);
} else if ($disable_header === false && !$show_header) {
    $package->getConfig()->save('front_end.show_header', true);
}


Hope this helps!
ConcreteConversion replied on at Permalink Reply
ConcreteConversion
I didn't know that a config key needed to be in the format "group.item" for 5.7, this will probably break some backwards-compatibility (as it did for me, my old keys didn't have that dot).

Also noticed that DatabaseSaver::save doesn't return a bool, so Repository::save will always return false.
jakobfuchs replied on at Permalink Reply
jakobfuchs
Is there any documentation on this subject yet? If not, would someone who understands the config system be willing to create a quick howto outlining how to 'CRUD' custom config values in packages or in general?

To give an example of how I'd like to use the Config system:

Let's say I have a package and want to install some 'options', whatever these may be. Then I would want to retrieve those options on a custom dashboard page, make changes to them and save them. When the package is uninstalled I want to delete the config keys/values without a trace.

A few questions, observations and problems that came up while trying the above:

- I don't really care where the values are stored (db or file)
- Not sure if storing to file or db is the better choice
- I wasn't able to delete a file config that was saved in /application/config/generated_overrides
- Why have two different ways of storing config values, is there any benefit of using the package specific implementation?
- Should I use save or set in scenarios where the outcome would be the same (e.g. in a package's on_start method)
- Couldn't figure out where the package config values are stored (not in the Config or ConfigStore table)
- Wouldn't the Config system be ideal to replace a lot of things that are traditionally implemented with Attributes? (assuming you could assign them to pages/users etc.)

The whole system appears pretty complicated to me for something as simple as storing a value, but that may be caused by my ignorance ;)
mesuva replied on at Permalink Reply
mesuva
My understanding of how to use the config system now in 5.7 is (in my opinion) even simpler than it was before.

There's really only two or three things you'd need to know.
// save a config value
\Config::save('packagename.key', $someValue));
// retrieve a config value
$value = \Config::get('packagename.key'));
// remove a config value
\Config::clear('packagename.key'));

This is going to create a file called packagename.php in /application/config/generated_overrides/ to store the values.

You can call these pretty much anywhere it makes sense to I believe. I just put them in single page controller methods and in the package install function for some defaults.

I've not used the database store in 5.7 at this point, only the file based version (i.e. the default). Personally I like the file approach as I'm under the impression that it's faster, but it's also easier to manually edit or clear. I'm using the above code in several packages and it works well.

When you uninstall a package I'm pretty sure you could just call \Config::clear on your config values to remove them. The file probably will stick around but just contain an empty array.
ramonleenders replied on at Permalink Reply
ramonleenders
What I'd like to know is, what do the core developers advice to use? Because there is a database config table as well. So, what should we use? Which method has which use cases? Or do we always need to use method X or Y?
jakobfuchs replied on at Permalink Reply
jakobfuchs
Yes that works pretty well, thanks. I am still not entirely sure what the best approach is, though. It would be a nice touch if Config would be integrated in the Package system, so that the keys/values get removed automatically when the package is uninstalled (like Themes and Block Types; I think that functionality is also missing for Topics).
Korvin replied on at Permalink Reply
Korvin
This will work because you're creating a new config group "packagename" and you're accessing the config item "key". However this is not necessary, we manage setting up a package namespace for you and give you an object that you can work with without needing to deal with global config. You can use that (as I said in my previous reply) like this:

$config = $pkg->getConfig();
$config->save('group.item', 'some value');


Now if I want to see what's saved for that config key, I just do:

$config = $pkg->getConfig();
$value = $config->get('group.item', $defaultValue);


Using this makes it a little simpler since you don't have to include your package name. Just group your config items by a common group for what they are used for. For example an authentication package might use a config key like "auth.oauth.key" and "auth.oauth.secret" where "auth" is the group and "oauth.key" is the config item.

concrete5 will manage keeping your package config separate from other people's package config, and someone who wants to override the setting programmatically can do so just by doing:

$package = Package::getByHandle('your_package');
$value = $package->getConfig('auth.oauth.secret');



----------------------

Now if you want to save your config to the filesystem and not in the database so that you can maybe create a file like what we have in /application/config, you can use `$pkg->getFileConfig()` instead of just ->getConfig.

So if I have a file like this in my package:

<?php
// /packages/my_package/config/auth.php
return [
    'oauth' => [
        'key' => 'somekey',
        'secret' => 'somesecret'
    ]
];


I can do this in my package controller:

$config = $this->getFileConfig();
$oauthConfig = $config->get('auth.oauth'); // this equals [ 'key' => '...', 'secret' => '...' ]


Hope that helps clear this up!
andrew replied on at Permalink Reply
andrew
We will add a nicer syntax for saving these config values as well. It may not live in the Package object as before, but something where you can pass a package as a first value to a Config object and the value you want to save as the second (so you don't have to know about the namespace rules)
goutnet replied on at Permalink Reply
goutnet
Would you be opposed to a pull-request adding back the config() and saveConfig() method to the package ?
Korvin replied on at Permalink Reply
Korvin
@goutnet, I'd like to put more thought into it than that. Those method signatures are pretty rough, I'm hoping to do something like:

<?php
namespace ..;
class Package
{
...
    public function getConfig()
    {
        if (!$this->config) {
            $this->config = \Config::getNamespacedRepository($this->getPackageHandle());
        }
        return $this->config;
    }
...
}


That way we are hopefully keeping this functionality outside of the package object.


$this->getConfig()->save('group.key', $value);
goutnet replied on at Permalink Reply
goutnet
I added that API here :

https://github.com/goutnet/concrete5-5.7.0/tree/add-config-package-a...

see:

https://github.com/goutnet/concrete5-5.7.0/blob/be6e57ded24dcd70890f...


but for some reason the namespace is ignored in database (seehttps://github.com/concrete5/concrete5-5.7.0/blob/master/web/concret... method addNamespace)

so for now the namespace is not added upon Package installation … I am not sure I am right or not … Should I correct that too? or am I missing something?
goutnet replied on at Permalink Reply
goutnet
Nevermind that, my db was broken :/ I checked on a fresh install it now works perfectly.

I created a pull request to add that API:

https://github.com/concrete5/concrete5-5.7.0/pull/1102...