dgvirtual/codeigniter4-meta-info

Codeigniter4 library enabling use of Entity-Attribute-Value style storage for additional fields of models.

dev-develop 2025-02-22 19:21 UTC

This package is auto-updated.

Last update: 2025-04-22 19:40:42 UTC


README

CodeIgniter4 Meta Info library enables use of Entity-Attribute-Value style storage for additional data of entities used with your models. Library is based on code from Bonfire2 project. It allows storing user-configurable bits of information for your project's entity classes without the need to modify those classes.

PHPUnit Coverage PHPStan PHP CodeIgniter GitHub license contributions welcome

Usage case

Lets say you have a users table to store data of users. Suppose you need to add aditional fields to the user table to store additional information, whether this is a bio, a website URL, social links, or anything else.

You can either change the main table each time you need such changes, or, alternatively, – store such data in a separate table, without constantly changing the database schema.

Using this library you can add additional information to a user. Moreover, such data can be seamlessly integrated into the Create/Edit User form so you do not have to modify that one manually on each change of user data schema.

Installation

Install via Composer (you will have to set minimum stability to dev, as this is pre-release library):

composer config minimum-stability dev
composer require dgvirtual/codeigniter4-meta-info

Then run the migration to setup the database table meta_info (assuming you already configured your database):

php spark migrate -n \Dgvirtual\MetaInfo

Setup 1. Defining Meta Fields

If you want to get a functionality preview, you can enable the demo code (read the section demo below).

First you create a config class for your users table, app/Config/Users.php. Put a property $metaFields into that class:

public $metaFields = [
    'Social Links' => [
        'blog' => [
            'label' => 'Blog',
            'type'  => 'text',
            'validation' => 'permit_empty|valid_url_strict'
        ],
    ],
];

In the above example:

  • Social links is a subcategory that can later be used to build a categorized view for the data;
  • the key type corresponds to the field type (text, checkbox, etc.) in the view (Supported type values: checkbox, textarea, text, and other "text" variants: number, password, email, tel, url, date, time, week, month, color).
  • blog is the column for our data.

The other fields are used for data validation:

  • label will act as label for validation rule and a label for HTML input, and
  • validation key => value pair will be transformed into a rules with validation rules as value.

Setup 2. Adding Trait to your Entity class

Add the HasMeta trait and a protected $configClass property with a string value, the name of the above-mentioned Config class containing $metaFields array, to the Entity class that represents your resource.

use Dgvirtual\MetaInfo\Traits\HasMeta;
use CodeIgniter\Entity;

class User extends Entity
{
    use HasMeta;

    protected string $configClass = 'Users';
}

Manipulating data

The User entity has a trait applied, HasMeta, that provides all the functionality you should need to work with the meta information for that user.

meta(string $key)

This returns the value of the user's meta named $key, or null if nothing has been set for that user. The name is the key of the array mentioned above.

$blog = $user->meta('blog');

allMeta()

This returns all meta fields for this user. Note that it returns the full database results, not just the name/value.

$meta = $user->allMeta();

var_dump($meta);

// Returns:
[
    'resource_id' => 123,
    'class' => 'App\Entities\User',
    'key' => 'blog',
    'value' => 'http://example.com',
    'created_at' => '2025-01-12 12:31:12',
    'updated_at' => '2025-01-12 12:31:12',
]

hasMeta(string $key)

Used to check if a user has a value set for the given meta field.

if ($user->hasMeta('foo')) {
    //
}

saveMeta(string $key, $value)

Saves a single meta value to the user. This is immediately saved. There is no need to save the User through the UserModel.

$url = $this->request->getPost('blog');
$user->saveMeta('blog', $url);

deleteMeta(string $key)

Deletes a single meta value from the user. This is immediately deleted. There is no need to save the User through the UserModel.

$user->deleteMeta('blog');

deleteResourceMeta()

Deletes all meta info associated with an entity. To be used when purging a record.

$user->deletResouceMeta();

syncMeta(array $post)

Given an array of key/value pairs representing the name of the meta field and it's value, this will update existing meta values, insert new ones, and delete any ones that were not passed in. Useful when grabbing the information from a form and updating all the values at once.

$post = [
    'blog' => 'http://example.com',
    'fb' => 'johnny.rose'
];
$user->syncMeta($post);

syncMeta() will also delete meta data for the resource that is present in meta_info but not present in the corresponding config file's $metaFields property, and therefore not used (usually happens if you change $metaFields at some point, thus orphaning some data).

metaValidationRules(string $prefix=null)

This examines the specified config file and returns an array with the names of each field and their validation rules, ready to be used within CodeIgniter's validation library. If your form groups the name as an array, (like meta[blog]) you may specify the prefix to append to the field names so that validation will pick it up properly.

$rules = $user->metaValidationRules('meta');

var_dump($rules);

// Returns:
[
    'meta.blog' => 'required|string|valid_url',
]

Using views for meta info display/editing

You can use directly (or copy and adapt) views in src/Views/ to include the meta info in your CRUD views as, for example, view cells, for display (meta_display) or editing (meta_edit). See how that is done in the Demo code.

Using the library for searches

You will want to get the data from meta_info table the same way you would from a related table, and, for example, display it in search results.

Of course that is not as simple as when using a simple join.

To do that you will need to employ WithMeta trait in your model:

namespace App\Models;

use App\Entities\User;
use CodeIgniter\Model;
use Dgvirtual\MetaInfo\Traits\WithMeta;

class UserModel extends Model
{
    use WithMeta;
    // other code
}

Now you can use the methods provided by WithMeta to build queries. For example, you can write the search() method in your model employing the trait methods generateMetaSelectClause, joinMetaInfo and orLikeInMetaInfo() when constructing the query; for example:

public function search(string $term, int $limit = 100, int $offset = 0): array
{
    $termInMeta = config(\Config\Users::class)->includeMetaFieldsInSearch;

    // First: get the expanded select clause that includes info from meta_info table
    $selectClause = $this->generateMetaSelectClause($termInMeta, \App\Entities\User::class);

    $query = $this->select($selectClause)->distinct();

    if (!empty($termInMeta)) {
        // Second: generate the join statement
        // here the User::class is string representation of the entity class
        $query->joinMetaInfo(\App\Entities\User::class, $this->table);
    }

    if ($term) {
        $query->like('first_name', $term, 'right', true, true)
                ->orLike('last_name', $term, 'right', true, true)
                ->orLike('username', $term, 'right', true, true);

        if (!empty($termInMeta)) {
            foreach ($termInMeta as $metaField) {
                // Third: perform the search through like statements
                $query->orLikeInMetaInfo($metaField, $term, 'both', true, true);
            }
        }
    }

    return $query->findAll($limit, $offset);
}

This method can now be used in controllers to perform searches and get information with data from meta_info table neatly integrated into the data from the main table.

Demo

A demo is provided with this library. Enabling demo would create a table testusers in your DB, which you can remove later.

Steps to enable the demo:

  1. update Config\Autoload class to include demo namespace into the list of available namespaces:

    public $psr4 = [
        APP_NAMESPACE    => APPPATH,
        // add two lines temporarily:
        'Dgvirtual\Demo' => ROOTPATH . 'vendor/dgvirtual/codeigniter4-meta-info/demo',
        'Tests\Support'  => ROOTPATH . 'vendor/dgvirtual/codeigniter4-meta-info/tests/_support',
    ];
  2. Setup the database if you have not done so already, then migrate and seed the demo table:

    php spark migrate -n \Dgvirtual\MetaInfo
    php spark migrate -n \Dgvirtual\Demo
    php spark db:seed \Tests\Support\Database\Seeds\TestusersSeeder
    
  3. Copy the routes you will need into your Config\Routes.php file:

    $routes->group('testusers', ['namespace' => 'Dgvirtual\Demo\Controllers'], static function ($routes) {
        $routes->get('', 'TestusersController::index');
        $routes->get('create', 'TestusersController::create');
        $routes->get('edit/(:num)', 'TestusersController::edit/$1');
        $routes->get('display/(:num)', 'TestusersController::display/$1');
        $routes->post('save', 'TestusersController::save');
        $routes->post('save/(:num)', 'TestusersController::save/$1');
        $routes->post('delete/(:num)', 'TestusersController::delete/$1');
    });
    $routes->get('meta_info', '\Dgvirtual\Demo\Controllers\MetaInfoController::index');

    Now you can run php spark serve and open the demo at http://localhost:8080/testusers

  4. To modify the meta fields assigned to the testuser you can copy the demo's file Config/Testusers.php to app/Config, change it's namespace to Config and modify it to your liking; the changes will be reflected in the demo's create and edit pages. The display page does not use a dynamic template, so it will remain the same.

To disable the demo, please undo the above-mentioned changes in files. To remove the demo table, use Codeigniter4 migration rollback functionality.

Contributing

If you would like to contribute to this project, please fork the repository and submit a pull request.

Credits

This library was created by Donatas Glodenis. You can reach out to me at [dg@lapas.info] for any questions or feedback.

The library started as an adaptation of Bonfire2 Users meta info functionality for general CodeIgniter 4 use. Bonfire2 was created by Lonnie Ezell lonnieje@gmail.com and contributors. For more information, visit the Bonfire2 project.

License

This project is licensed under the MIT License. See the LICENSE file for details.