Data Manager

Simple data manager for nested data, dot notation access, extendability, and container interoperability.

See Also the Auto Generated API Documentation

Goals

  • Light weight with a fluent, simple, clear API
  • Manage any data type (closure, object, primitives, etc.)
  • Manage nested data via dot-notation (one.two.three)
  • Manage nested data via magic methods ($manager->one()->two()->three), if desired
  • Be composable - integrate into current containers via traits
  • Be extensible.
  • Allow for protected data (immutable)
  • Test coverage, PSR compliant, container interoperability, and best practices

Extras

On top of being a powerful data-manager, there are traits that add features. Please see Composing for information about mixing and matching features or integrating different traits into your project.

  • Arrayable: Use Manager as an array.

  • Chain Access: Access data through $manager->method()->chaining()->for()->value

  • IoC Container: Use Manager as a simple but powerful Dependency Injection Container. Includes:

    • Resolving dependencies from classnames, closures, eager loading, and more.
    • Creating singletons.
    • Configuring dependencies for dependencies.
    • Fallbacks, preparing objects, and more.
    • Use Manager as a configuration bank, complete with defaults.
    • Load configuration files (php, yaml, json, xml, and custom)
  • Collections: Adds extra array-helper methods (based on Arrayzy)

  • Load From Files: Load from various types of files. Json, Php, and Yaml supported by default. Add your own Custom Decoders easily. Also allows for namespacing items under a safe file name. Great for a configuration bank.

Install

Via Composer

$ composer require michaels/data-manager

Getting Started

Manager does exactly what you would expect: it manages complex items such as config data, arrays, and closures. The best way to get started is simply instantiate Michaels\Manager\Manager

$manager = new Michaels\Manager\Manager([
    'some' => [
        'starting' => [
            'data' => 'here (optional)'
        ]
    ]
]);
// Note, you may initialize Manager with an array or any instance of Traversable (like Manager itself)

/* Basic Usage. All works with dot notation as well */
$manager->add('name', 'value');
$manager->add('some.nested.data', 3); // Use dot notation for namespacing or nesting
$manager->get('name'); // 'value'
$manager->get('doesntexist', 'fallback'); // 'fallback'
$manager->get('doesntexist') // throws an ItemNotFoundException with no fallback
$manager->getIfHas('doesntexist') // returns a NoItemFoundMessage instead of a script-stopping exception
$manager->getAll(); // returns array of all items
$manager->all(); // returns array of all items
$manager->exists('name'); // true
$manager->exists('some.starting.data'); // true
$manager->exists('nope'); // false
$manager->has('something'); // alias of exist
$manager->set('name', 'new-value'); // updates item
$manager->remove('some.starting.data');
$manager->isEmpty(); // true or false
$manager->toJson(); // returns json of all items
echo $manager; // returns json string of all items
$manager->reset($array); // rebuild with new items
$manager->clear(); // empty the manager

Protecting Data

You can also guard any item or nest from being changed. Simply,

$manager->protect('some.data'); //now some.data and everything under it cannot be altered
$manager->set('some.data.here', 'new-value'); // throws an exception

Merging Defaults Into Current Dataset

When using Manager to store configuration data, it is important to be able to set defaults. You can merge an array of defaults into manager via loadDefaults(array $defaults)

Imagine your configuration starts like

$manager = new Manager([
    'name' => 'My Awesome App',
    'site' => [
        'url' => 'https://youwishyouwerethiscool.com/',
        'protocol' => 'https',
    ]
]);

But your app needs site.assets for the assets directory. Simply

$manager->loadDefaults([
    'site' => [
        'url' => 'http://the_default_url.com/',
        'assets' => '/assets',
    ],
    'database' => "mysql"
]);

And now, your configuration looks like

    'name' => 'My Awesome App',
    'site' => [
        'url' => 'https://youwishyouwerethiscool.com/'
        'protocol' => "https",
        'assets' => '/assets'
    ],
    'database' => "mysql"

A couple of things to keep in mind: * This works recursively and as far down as you want. * If any value is set before loading defaults, that value is preserved * If a starting value is set to an array (one.two = []) and a default lives beneath (one.two.three = default), then the default will be set. * On the other hand, if the value exists and is not an array, the default will be ignored. (one.two = 'something') In this case, there is no one.two.three, even after loading defaults.

Using Other Managers

Each feature of Manager is a different trait, but they are all designed to work in whatever combination you want. You can either composer your own or use one of the build in classes - Basic Manager: Only manages items (what you see above). - Manager: The basic manager with array access and the ability to chain nested items - Config Manager: Manages items like above, loads config files, allows for defaults. - IoC Manager: Manages an IoC (DI) container. You can resolve dependencies, etc. - Uber Manager: Everything in one place. Just for fun, really.

Some Advanced Features

By default, Manager stores all the items in an $items property. If you are using the ManagesItemsTrait and want to use an internal property besides $items to avoid collisions, you have two options:

  1. Use $manager->setItemsName($nameOfProperty) either in your constructor or before you add anything
  2. Set the $dataItemsName property to a string of the new property name. Then be sure to call initManager() in your constructor.

Exceptions

If you try to get() an item that doesn't exist, and there is no fallback, an ItemNotFoundException will be thrown.

If you do not want an exception, use getIfHas($alias) which will return a NoItemFoundMessage object, or use a fallback value get($item, $fallback).

If you try to nest under an existing value that is not an array, an NestingUnderNonArrayException will be thrown.

$manager = new Manager(['one' => 1]);
$manager->add("one.two", "two-value"); // exception

If you try to alter a protected item, a ModifyingProtectedItemException will be thrown.

See /exceptions for more

Interoperability

Data Manager is PSR compliant and Container Interoperability compliant. Any oversights, please let me know.

Testing

We try for at least 80% test coverage.

$ phpunit

You may also use the tests under tests/traits to test your integrated functionality. You may have to grab these through cloning the repo. composer usually won't include tests in your require

Contributing

Contributions are welcome and will be fully credited. Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email chrismichaels84@gmail.com instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.