Fork me on GitHub

Mesour Editable

Edit Doctrine entity with all collections or use it without Doctrine :) Simple way and settings...

Packagist Build Status

  • jQuery 2.0+ Download
  • Twitter bootstrap 3.2+ Download
  • Bootstrap 3 Datepicker (in default, can change) v4 Download
  • mesour.components.min.js
  • mesour.modal.min.js Download
  • mesour.editable.min.js
  • None

Installation

Suggests

  • None

Live demo

Events

Event onEditField

FIRED if field is edited
$grid->onEditField[] = function (
    Mesour\Editable\Structures\Fields\IStructureField $field,
    $newValue,
    $oldValue,
    $identifier = null,
    array $params = []
) {
    //! do some SQL for save
};
Argument Type Description
$field Mesour\Editable\Structures\Fields\IStructureField Current structure field
$newValue mixed New value
$oldValue mixed Old value
$identifier mixed string
$params array Parameters for field

Event onEditElement

FIRED if element is edited
$grid->onEditElement[] = function (
    Mesour\Editable\Structures\Fields\IStructureElementField $field,
    array $values,
    array $oldValues,
    Mesour\Editable\Structures\Reference $reference,
    $identifier = null,
    array $params = []
) {
    //! do some SQL for save
};
Argument Type Description
$field Mesour\Editable\Structures\Fields\IStructureField Current structure field
$values array New value
$oldValues array Old value
$reference Mesour\Editable\Structures\Reference Reference settings
$identifier mixed string
$params array Parameters for field

Event onCreate

FIRED if element is created
$grid->onCreate[] = function (
    Mesour\Editable\Structures\Fields\IStructureElementField $field,
    array $newValues,
    $identifier = null,
    array $params = []
) {
    //! do some SQL for insert
};
Argument Type Description
$field Mesour\Editable\Structures\Fields\IStructureField Current structure field
$newValues array New value
$identifier mixed string
$params array Parameters for field

Event onAttach

FIRED if element is attached (only if use many to many field)
$grid->onAttach[] = function (
    Mesour\Editable\Structures\Fields\IStructureElementField $field,
    Mesour\Editable\Structures\Reference $reference, 
    $identifier = null,
    array $params = []
) {
    //! do some SQL for save
};
Argument Type Description
$field Mesour\Editable\Structures\Fields\IStructureField Current structure field
$reference Mesour\Editable\Structures\Reference Reference settings
$identifier mixed string
$params array Parameters for field

Event onRemove

FIRED if element is removed
$grid->onRemove[] = function (
    Mesour\Editable\Structures\Fields\IStructureElementField $field,
    $value,
    $identifier = null
) {
    //! do some SQL for delete
};
Argument Type Description
$field Mesour\Editable\Structures\Fields\IStructureField Current structure field
$value mixed ID for removed item
$identifier mixed string

Validation

Info For validation throw Mesour\Editable\ValidatorException. Can too specify field name using method setFieldName on this exception.
$editable->onEditField[] = function (
    \Mesour\Editable\Structures\Fields\IStructureField $field,
    $newValue,
    $oldValue,
    $identifier = null,
    array $params = []
) use ($context, $source) {
    if ($field->getName() === 'group_name') {
        //! one to one relation
        $data = [
            'group_id' => $newValue,
        ];
    } else {
        //! validation here
        if ($field->getName() === 'email' && !\Nette\Utils\Validators::isEmail($newValue)) {
            $exception = new \Mesour\Editable\ValidatorException('Value must be valid email.');
            $exception->setFieldName($field->getName()); //! specify field (best in forms)
            throw $exception;
        }
        $data = [
            $field->getName() => $newValue,
        ];
    }

    //! here update in DB
};

Usage

IN php

Info For example with NetteDbSource

1. Create selection

$selection = $context->table('users');
$selection->select('users.*')
    ->select('group.name group_name')
    ->select('group.type group_type')
    ->select('group.date group_date');

2. Create source

$source = new \Mesour\Sources\NetteDbTableSource('users', 'id', $selection, $context);

$source->addTableToStructure('companies', 'id');

3. Create source structure

$dataStructure = $source->getDataStructure();

$dataStructure->addOneToOne('group_name', 'groups', 'name');
$dataStructure->addOneToOne('group_type', 'groups', 'type');
$dataStructure->addOneToOne('group_date', 'groups', 'date');

$dataStructure->addOneToMany(
    'addresses',
    'user_addresses',
    'user_id'
);

$dataStructure->addManyToMany(
    'companies',
    'companies',
    'company_id',
    'user_companies',
    'user_id'
);

4. Create editable structure from source

$structure = \Mesour\Editable\Structures\DataStructure::fromSource($source);

$groupsElement = $structure->getElement('groups');

$groupsElement->addText('name', 'Name');

$groupsElement->addText('type', 'Type');

$groupsElement->addDate('date', 'Date')
    ->setFormat('Y-m-d H:i:s');

$groupsElement->addNumber('members', 'Members')
    ->setUnit('EUR')
    ->setDecimals(2)
    ->setDecimalPoint(',')
    ->setThousandSeparator('.');

$addressesElement = $structure->getElement('user_addresses');

$addressesElement->addText('street', 'Street');
$addressesElement->addText('city', 'City');
$addressesElement->addText('zip', 'Zip');
$addressesElement->addText('country', 'Country');

$companiesElement = $structure->getElement('companies');

$companiesElement->addText('name', 'Name');
$companiesElement->addText('reg_num', 'Reg. number');
$companiesElement->addBool('verified', 'Verified');

//! foreach if you need more rows on one page
foreach ($source->fetchAll() as $values) {
    $id = $values['id'];

    $structure->addText('name', 'Name', $id);

    $structure->addNumber('amount', 'Amount', $id)
        ->setUnit('EUR')
        ->setDecimalPoint(',')
        ->setThousandSeparator('.')
        ->setDecimals(2);

    $structure->addDate('last_login', 'Last login', $id)
        ->setFormat('Y-m-d H:i:s');

    $structure->addEnum('role', 'Role', $id)
        ->addValue('admin', 'Admin')
        ->addValue('moderator', 'Moderator');

    $structure->addBool('has_pro', 'Has PRO', $id);

    $structure->addOneToOne('group_name', 'Group', $id)
        ->enableCreateNewRow();

    $structure->addOneToMany('addresses', 'Addresses', $id);

    $structure->addManyToMany('companies', 'Companies', $id);
}

5. Create Application

$application = new \Mesour\UI\Application('mesourApp');

$application->setRequest($_REQUEST);

$application->run();

6. Create and render editable

$editable = new \Mesour\UI\Editable('editableTest', $application);

$editable->setDataStructure($structure);

$editable->onCreate[] = function () {
    /*$exception = new \Mesour\Editable\ValidatorException('Field name is required');
    $exception->setFieldName('name');
    throw $exception;*/
};

echo $editable->render();

In HTML

Info This example only for static first item with ID = 1
<div class="jumbotron"<?php echo $editable->createSnippet()->attributes(); ?>>

    <p data-editable="name" data-id="1">John</p>
    <p data-editable="amount" data-id="1">100 EUR</p>
    <p data-editable="last_login" data-id="1">2016-01-01 20:00:00</p>
    <p data-editable="role" data-id="1" data-value="admin">Admin</p>
    <p data-editable="has_pro" data-id="1" data-value="1">Yes</p>
    <p data-editable="group_name" data-id="1" data-value="1">First group</p>

    <div>
        <ul>
            <li data-editable="addresses" data-no-action="true">
                <span data-editable="addresses" data-id="1" data-value="10">Test 1, 12345 Hehehov, CZ</span>
                <a href="#" class="fa fa-remove" data-editable="addresses" data-id="1" data-value="10"
                   data-is-remove="true"></a>
            </li>
            <li>
                <a data-editable="addresses" data-is-add="true" data-id="1" class="btn btn-warning btn-sm">
                    <i class="fa fa-plus"></i>
                    Add new address
                </a>
            </li>
        </ul>
    </div>

    <div>
        <ul>
            <li data-editable="companies" data-no-action="true">
                <span data-editable="companies" data-id="1" data-value="5">IBM CZ s.r.o.</span>
                <a href="#" class="fa fa-remove" data-editable="companies" data-id="1" data-value="5"
                   data-is-remove="true"></a>
            </li>
            <li>
                <a data-editable="companies" data-is-add="true" data-id="1" class="btn btn-warning btn-sm">
                    <i class="fa fa-plus"></i>
                    Add new company
                </a>
            </li>
        </ul>

    </div>

</div>

In JavaScript

Info Only example for previous HTML. Can customize everything ;-)
$(function () {
    var COMPONENT_NAME = 'mesourApp-editableTest';

    $(document).on('click', '[data-editable]', function (e) {
        var $this = $(this);

        if ($this.attr('data-no-action')) {
            return;
        }

        if (e.ctrlKey) {
            e.preventDefault();

            var name = $this.attr('data-editable'),
                isAdd = $this.attr('data-is-add'),
                isRemove = $this.attr('data-is-remove'),
                value = $this.attr('data-value'),
                id = $this.attr('data-id');

            if (isAdd) {
                mesour.editable.getComponent(COMPONENT_NAME).newEntry(name, $this, id);
            } else if (isRemove) {
                mesour.editable.getComponent(COMPONENT_NAME).remove(name, $this, id, value);
                $('[data-editable=' + name + ']').not('[data-is-add]').remove();
            } else {
                mesour.editable.getComponent(COMPONENT_NAME).edit(name, $this, id, value);
            }
        }
    });
});

Demo

This is demo only, data are not saved. For full demo run /examples/index.php in root of mesour/editable package
Use ctrl+click to edit

User detail

Name Kathy
Surname Arnold
Email kathy.arnold@test.xx
Birth date 2014-09-07 10:24:07
Addresses
Amount 456.987 EUR
Role admin
Group Group 1
Has PRO No
Companies