ATK Data - Data Access Framework for high-latency databases (Cloud SQL/NoSQL).

Overview

ATK Data - Data Model Abstraction for Agile Toolkit

Agile Toolkit is a Low Code framework written in PHP. Agile UI implement server side rendering engine and over 50 UI generic components for interacting with your Data Model.

Agile Data is a framework for defining your "business layer" which is separate from your "presentation layer" and "persistence". Together with Agile UI you can deliver user interface "out of the box" or with Agile UI - general-purpose API endpoints.

  • Agile Data uses PHP to define your Business Objects, their properties and actions.
  • Agile Data works with SQL, NoSQL or external API sources.
  • Agile Data plugs into generic UI components (Crud, Card, Table, Form, etc) with a minimum code.
  • Agile Data supports "user actions". UI layer uses "action executor" to read ACL-controlled metadata.
  • Agile Data is developer-friendly and easy to learn
  • Agile Data is high-performance, capable of abstracting aggregation logic and shifting it into a capable database persistence (such as SQL) through advanced expressions.
  • Agile Data is extensible - field types, persistence types, relations and action types can be extended.

Build CodeCov GitHub release Code Climate

Quick-Links: Documentation. Namespaces. Example. ATK UI. Forum. Chat. Commercial support. Udemy Course.

Is ATK Data similar to ORM?

Yes and no.

Agile Data is data persistence framework - like ORM it helps you escape raw SQL. Unlike ORM, it maps objects into "data set" and not "data record". Operating with data sets offers higher level of abstraction:

$vip_clients = (new Client($db))->addCondition('is_vip', true);

// express total for all VIP client invoices. The value of the variable is an object
$total_due = $vip_clients->ref('Invoice')->action('fx', ['sum', 'total']);

// Single database query is executed here, but not before!
echo $total_due->getOne();

In other ORM the similar implementation would be either slow, clumsy, limited or flawed.

How ATK Data integrates with UI (or API)

Agile Toolkit is a low-code framework. Once you have defined your business object, it can be associated with a UI widget:

$app->add(new Crud())->setModel(new Client($db), ['name', 'surname'], ['edit', 'archive']);

or with an API end-point:

$api->rest('/clients', new Client($db));

Extensibility and Add-ons

ATK Data is extensible and offers wide range of add-ons ranging from Audit and Aggregation/Reporting. Developer may also implement advanced DB concepts like "disjoint subtypes" - allowing to efficiently persist object-oriented data in your database.

Regardless of how your model is constructed and what database backend is used, it can easily be used in conjunction with any 3rd party add-on, like Charts.

Benefits of using ATK Data

Designed for medium to large PHP applications and frameworks, ATK Data is a clean implementation of Data Mapper that will:

  • Make your application really database-agnostic. SQL? NoSQL? RestAPI? Cache? Load and store your data with any of these, without refactoring your code.
  • Execute more on the server. Agile Data converts query logic into server-specific language (e.g. SQL) then delivers you the exact data rows / columns which you need from a single statement, no matter how complex.
  • Data architecture transparency. As your database structure change, your application code does not need to be refactored. Replace fields with expressions, denormalize/normalize data, join and merge tables. Only update your application in a single place.
  • Extensions. "Audit" - transparently record all edits, updates and deletes with "Undo" support. "Reports" - add conditions, group results, union results then group them again, join add limit for a great report design.
  • Out of the box UI. Who wants to build Admin systems today? Tens of professional components: Crud, Grid, Form as well as add-ons like Charts can be added to your PHP app with 3-lines of code.
  • RestAPI server for Agile Data is currently under development.
  • Agile Data and all extensions mentioned above are licensed under MIT and are free to use.

Since the initial introduction of Agile Data back in 2016 our group of early-adopters used it in large production PHP projects. It is time for you to try Agile Data today.

Getting Started

Watch Quick Start or Screencasts. There is also our Official Udemy Course and Full Documentation (PDF).

ATK Data relies on ATK Core and can be greatly complimented by ATK UI:

  • Agile Core - documents various low-level traits and features such as Containers, Hooks or Exceptions (PDF)
  • Agile UI - documents optional UI components and how to build Web App with them. (PDF)

When to use Agile Data?

We believe that as a developer you should spend your time efficiently. Heavy-lifting your data is not efficient. Agile Data enables UI components, exporters, importers or RestAPI servers to be implemented in a generic way.

HTML Applications and Admin Systems

Most of the ORM (including the one you are probably using now) suffer from one flaw. As a framework they do not have enough information to describe models, fields, relations and conditions of your data model.

integration-orm

As a result the UI layer cannot simply discover how your Invoice relate to the Client. This makes YOU write a lot of glue code - performing query and feeding data into the UI layer.

With most ORMs you cannot design an generic Crud or Form which would work with ANY model. As a result server-side rendering becoming more extinct in the face of Client-side frameworks.

Agile Data addresses this balance. For the presentation logic you can use tools such as Agile UI, that consists of generic Crud, Form implementations or other modules which accept the Model protocol of Agile Data:

$presentation->setModel($business_model);

This now re-shifts the balance and makes it possible to implement any generic UI Components, that will work with your custom data model and your custom persistence (database).

integration-atk

It's important to note, that glue may also interact with the model preparing it for a specific use-case:

$grid = new \Atk4\Ui\Table();
$data = new Order($db);
$data->addCondition('is_new', true);
$data->addCondition('client_id', $_GET['client_id']);
$grid->setModel($data);

$html = $grid->render();

Domain-Model Reports

Object Oriented approach was designed to hide the complexity of implementation. Yet, every time when you need data for the reports that include aggregation or joins, you must dive deep into your database structure to pull some quirky ORM hack or inject a custom SQL query.

Agile Data was designed in a way where all of your code can rely ONLY on model objects. This includes the reports.

This next example builds a complex "Job Profitability Report" by only relying on Model logic:

class JobReport extends Job {
  function init(): void {
    parent::init();

    // Invoice contains Lines that may relevant to this job
    $invoice = new Invoice($this->persistence);

    // We need to ignore draft invoices
    $invoice->addCondition('status', '!=', 'draft');

    // Each invoice may have multiple lines, which is what we want
    $invoice_lines = $invoice->ref('Lines');

    // Build relation between job and invoice line
    $this->hasMany('InvoiceLines', ['model' => $invoice_lines])
      ->addField('invoiced', ['aggregate'=>'sum', 'field'=>'total', 'type'=>'money']);

    // Next we need to see how much is reported through timesheets
    $timesheet = new Timesheet($this->persistence);

    // Timesheet relates to client. Import client.hourly_rate as expression.
    $timesheet->getRef('client_id')->addField('hourly_rate');

    // Calculate timesheet cost expression
    $timesheet->addExpression('cost', '[hours]*[hourly_rate]');

    // Build relation between Job and Timesheets
    $this->hasMany('Timesheets', ['model' => $timesheet])
      ->addField('reported', ['aggregate'=>'sum', 'field'=>'cost', 'type'=>'money']);

	// Finally lets calculate profit
    $this->addExpression('profit', '[invoiced]-[reported]');

    // Profit margin could be also useful
    $this->addExpression('profit_margin', 'coalesce([profit] / [invoiced], 0)');
  }
}

Your Report Model:

  • moves query logic to the database (SQL)
  • is still a model, so compatible with all UI Components and extensions

In order to output results on HTML table:

$grid = new \Atk4\Ui\Grid();
$data = new JobReport($db);
$grid->setModel($data);

$html = $grid->render();

Or if you want to display them as a Chart, using https://github.com/atk4/chart and https://github.com/atk4/report

$chart = new \Atk4\Chart\BarChart();
$data = new JobReport($db);

// BarChart wants aggregated data
$data->addExpression('month', 'month([date])');
$aggregate = new \Atk4\Report\GroupModel($data);
$aggregate->groupBy('month', ['profit_margin'=>'sum']);

// Associate presentation with data
$chart->setModel($aggregate, ['month', 'profit_margin']);
$html = $chart->html();

In both cases you end up executing just one SQL query.

Large Application and Enterprise use

Refactoring

One of the best benefits of Agile Data is ability to refactor database structure in a way which will not impact your application entirely. This severely simplifies your Q/A cycle and reduce cost of application maintenance. As example lets look at the following scenario:

The existing application calculates the profits based on a SQL formula, but the insane amount of data makes the calculation slow. The solution is to add a "profits" field which value would be automatically updated.

Agile Data gives you all the tools to do this in a few steps:

  • Update your Model definition by replacing "expression" with a regular field.
  • Create a "migrator" script which calculates expression using action.
  • Change model behaviors adding Model Hook (afterSave) to re-calculate "profit" within same ACID transaction.

This will not break the rest of your applications - UI, RestAPI or Reports will continue to work, but faster.

Audit and Customization

I explain some basic customization in the video: https://www.youtube.com/watch?v=s0Vh_WWtfEs&index=5&list=PLUUKFD-IBZWaaN_CnQuSP0iwWeHJxPXKS

There is also "Advanced Topics" section in the documentation: http://agile-data.readthedocs.io/en/develop/advanced.html

Multi-System Applications

Most SaaS systems have a requirement where user data may not be accessible by other users. Still, the data is stored in the same database and is only distinguished by "system_id" or "user_id" field.

Agile Data has a usage patters that will automatically restrict access by this conditions on all models. This will ensure that currently-logged-in user will be unable to add any data or access any data that does not belong to him even if developer makes a mistake in the code.

Migrating from one Database to Another and cross-persistence

With Agile Data you can move your data from one persistence to another seamlessly. If you rely on some feature that your new persistence does not support (e.g. Expression) you can replace them a callback calculation, that executes on your App server.

As usual - the rest of your application is not affected and you can even use multiple types of different persistences and still navigate through references.

Support

Because our team have implemented Agile Data, we have trained experts who can offer commercial consultancy, training and support. Use our Contact form: http://www.agiletoolkit.org/contact for inquiries.

Framework Integrations

Agile Data (and in some cases Agile UI) have been integrated by community with other popular frameworks:

Q&A

Q: I noticed that Agile Data uses sub-selects instead of JOIN. I believe JOIN is more efficient.

While in most cases modern SQL sub-queries have comparable speed to JOIN, Agile Data's SQL persistence also implements "JOIN" support. Use of SubQueries is safer by default because it can imply conditions on a related entity.

You can, however, import fields through joins too

Q: I don't like the $book->set('field', 123), I prefer properties

Agile Models are not Entities. They don't represent a single record, but rather a set of records. Which is why Model has some important properties: $model->getId(), $model->persistence and model->data.

Read more on working with individual data records.

Q: I do not like to use class \Atk4\Data\Model as a parent

Class Model implements a lot of essential functionality. As I mentioned before Model is not an Entity, so while iterating through result, no multiple Model instances are created. If you need a deeper explanation read my blog post: http://www.agiletoolkit.org/blog/why-should-you-extend-your-entity-class

Q: Agile Data has small community

This is something you can change. If you look at the features of Agile Data and believe that it deserves more attention, help us by spreading the word and growing our community.

Agile Data is still relatively new framework and it takes time until PHP community recognizes it.

Q: There is broken link / documentation / page

We put all our focus into making a good quality software and give it to you for free. We will try our best to address any broken pages / links or outdated pages, but our resources are limited.

Q: Is there training material for Agile Data / Agile UI

We are working on it. For now - visit our gitter.im.

Q: How can I help / Contribute?

Say hi. We enjoy meeting new people regardless of how good they are with PHP and the framework (https://gitter.im/atk4/atk4).

If you want to help, we have a special tag Help Wanted in our issue system:


some of the information below may be out of date and needs to be cleaned up.

Agile Data at a Glance

Agile Data implements various advanced database access patterns such as Active Record, Persistence Mapping, Domain Model, Event sourcing, Actions, Hooks, DataSets and Query Building in a practical way that can be easily learned, used in any framework with SQL or NoSQL database and meeting all enterprise-specific requirements.

You get to manipulate your objects first before query is invoked. The next code snippet will work with your existing database of Clients, Orders and Order Lines and will query total amount of all orders placed by VIP clients. Looking at the resulting query you will notice an implementation detail - Line total is not stored physically inside the database but is rather expressed as multiplication of price and quantity:

$m = new Client($db);
echo $m->addCondition('vip', true)
  ->ref('Order')->ref('Line')->action('fx', ['sum', 'total'])->getOne();

Resulting Query will always use parametric variables if vendor driver supports them (such as PDO):

select sum(`price`*`qty`) from `order_line` `O_L` where `order_id` in (
  select `id` from `order` `O` where `client_id` in (
    select `id` from `client` where `vip` = :a
  )
)

// :a is "Y"

Agile Data is not only for SQL databases. It can be used anywhere from decoding Form submission data ($_POST) or even work with custom RestAPIs. Zero-configuration implementation for "AuditTrail", "ACL" and "Soft Delete" as well as new features such as "Undo", "Global Scoping" and "Cross-persistence" make your Agile Data code enterprise-ready out of the box.

All of the above does not add complexity to your business logic code. You don't need to create XML, YAML files or annotations. There is no mandatory caching either.

My next example demonstrates how simple and clean your code looks when you store new Order data:

$m = new Client($db);
$m->loadBy('name', 'Pear Company');
$m->ref('Order')
   ->save(['ref'=>'TBL1', 'delivery'=>new DateTime('+1 month')])
   ->ref('Lines')->import([
      ['Table', 'category'=>'furniture', 'qty'=>2, 'price'=>10.50],
      ['Chair', 'category'=>'furniture', 'qty'=>10, 'price'=>3.25],
]);

Resulting queries (I have removed back-ticks and parametric variables for readability) use a consise syntax and demonstrate some of the "behind-the-scenes" logic:

  • New order must belong to the Company. Also company must not be soft-deleted.
  • delivery is stored in field delivery_date, also the DateTime type is mapped into SQL-friendly date.
  • order_id is automatically used with Lines.
  • category_id can be looked up directly inside the INSERT (standard feature of SQL reference fields).
select id, name from client where name = "Pear Company" and is_deleted = 0;
insert into order (company_id, ref, delivery_date)
  values (293, "TBL1", "2015-18-12");
insert into order_lines (order_id, title, category_id, qty, price) values
  (201, "Table", (select id from category where name = "furniture"), 2, 10.50),
  (201, "Chair", (select id from category where name = "furniture"), 19, 3.25);

If you have enjoyed those examples and would like to try them yourself, continue to https://github.com/atk4/data-primer.

Introducing Models

Agile Data uses vendor-independent and lightweight Model class to describe your business entities:

class Client extends \Atk4\Data\Model {
  public $table = 'client';
  function init(): void {
    parent::init();

    $this->addFields(['name','address']);

    $this->hasMany('Project', ['model' => [Project::class]]);
  }
}

Introducing Actions

mapping

Anything related to a Model (Field, Condition, Reference) is an object that lives in the realm of "Domain Model" inside PHP memory. When you save(), frameworks generates an "Action" that will actually update your SQL table, invoke RestAPI request or write that file to disk.

Each persistence implements actions differently. SQL is probably the most full-featured one:

GitHub release

Introducing Expressions

Smart Fields in Agile Toolkit are represented as objects. Because of inheritance, Fields can be quite diverse at what they do. For example FieldSqlExpression and Field_Expression can define field through custom SQL or PHP code:

GitHub release

Introducing References

Foreign keys and Relation are bread and butter of RDBMS. While it makes sense in "Persistence", not all databases support Relations.

Agile Data takes a different approach by introducing "References". It allow you to define relationships between Domain Models that can work with non-relational databases, yet allow you to perform various operations such as importing or aggregating fields. (use of JOIN is explained below)

GitHub release

Model Conditions and DataSets

Conditions (or scopes) are rare and optional feature across ORMs but it is one of the most significant features in Agile Data. It allows you to create objects that represent multiple database records without actually loading them.

Once condition is defined, it will appear in actions and will also restrict you from adding non-compliant records.

GitHub release

Build Reports inside Domain Model

With most frameworks when it comes to serious data aggregation you must make a choice - write in-efficient domain-model code or write RAW SQL query. Agile Data helps you tap into unique features of your DataBase while letting you stay inside Domain Model.

How do we create an efficient query to display total budget from all the projects grouped by client's country while entirely remaining in domain model? One line of code in Agile Data:

GitHub release

Did you notice the query has automatically excluded canceled projects?

Model-level join

Most ORMs can define models that only work with a single SQL table. If you have to store logical entity data into multiple tables - tough luck, you'll have to do some linking yourself.

Agile Data allow you to define multiple joins right inside your model. As you join() another table, you will be able to import fields from the joined table. If you create a new record, data will automatically be distributed into the tables and records will be linked up correctly.

GitHub release

The best part about joins is that you can add them to your existing model for specific queries. Some extensions can even do that.

Deep Model Traversal

Probably one of the best feature of Agile Data is deep traversal. Remember how your ORM tried to implement various many-to-many relationships? This is no longer a problem in Agile Data.

Suppose you want to look at all the countries that have 2-letter name. How many projects are there from the clients that are located in a country with 2-letter name?

Agile Data can answer with a query or with a result.

GitHub release

Advanced Features and Extensions

The examples you saw so far are only a small fragment of the possibilities you can achieve with Agile Data. You now have a new playground where you can design your business logic around the very powerful concepts.

One of the virtues we value the most in Agile Data is ability to abstract and add higher level features on our solid foundation.

Explorability

If you pass a $model object inside any method, add-on or extension, it's possible for them to discover not only the data, but also field types and various meta-information, references to other models, supported actions and many more.

With that, creating a Dynamic Form UI object that automatically includes Dropdown with list of allowed values is possible.

In fact - we have already stared work on Agile UI project!

Hooks

You now have a domain-level and persistence-level hooks. With a domain-level ones (afterLoad, beforeSave) you get to operate with your field data before or after an operation.

On other hand you can utilize persistence-level hooks ('beforeUpdateQuery', 'beforeSelectQuery') and you can interact with a powerful Query Builder to add a few SQL options (insert ignore or calc_found_rows) if you need.

And guess what - should your model be saved into NoSQL database, the domain-level hooks will be executed, but SQL-specific ones will not.

Extensions

Most ORMs hard-code features like soft-delete, audit-log, timestamps. In Agile Data the implementation of base model is incredibly lightweight and all the necessary features are added through external objects.

We are still working on our Extension library but we plan to include:

  • Audit Log - record all operations in a model (as well as previous field values), offers a reliable Undo functionality.
  • Reporting - offers UnionModel
  • ACL - flexible system to restrict access to certain records, fields or models based on permissions of your logged-in user or custom logic.
  • Filestore - allow you to work with files inside your model. Files are actually stored in S3 (or other) but the references and meta-information remains in the database.
  • Soft-Delete, purge and undelete - several strategies, custom fields, permissions.

More details on extensions: http://www.agiletoolkit.org/data/extensions

Performance

If you wonder how those advanced features may impact performance of loading and saving data, there is another pleasant surprise. Loading, saving, iterating and deleting records do not create new in-memory objects:

foreach($client->ref('Project') as $project) {
    echo $project->get('name')."\n"
}

// $project refers to same object at all times, but $project's active data
// is re-populated on each iteration.

Nothing unnecessary is pre-fetched. Only requested columns are queried. Rows are streamed and never ever we will try to squeeze a large collection of IDs into a variable or a query.

Agile Data works fast and efficient even if you have huge amount of records in the database.

Security

When ORM promise you "security" they don't really extend it to the cases where you wish to perform a sub-query of a sort. Then you have to deal with RAW query components and glue them together yourself.

Agile Data provides a universal support for Expressions and each expression have support for escaping and parameters. My next example will add scope filtering the countries by their length. Automatic parameters will ensure that any nastiness will be properly escaped:

$country->addCondition($country->expr('length([name]) = []', [$_GET['len']])); // 2

Resulting query is:

where length(`name`) = :a  [:a=2]

Another great security feature is invoked when you try and add a new country:

$country->insert('Latvia');

This code will fail, because our earlier condition that "Latvia" does not satisfy. This makes variety of other uses safe:

$client->load(3);
$client->ref('Order')->insert($_POST);

Regardless of what's inside the $_POST, the new record will have client_id = 3 .

Finally, the following is also possible:

$client->addCondition('is_vip');
$client->ref('Order')->insert($_POST);

Regardless of the content of the POST data, the order can only be created for the VIP client. Even if you perform a multi-row operation such as action('update') or action('delete') it will only apply to records that match all of the conditions.

Those security measures are there to protect you against human errors. We think that input sanitization is still quite important and you should do that.

Installing into existing project

Start by installing Agile Data through composer:

composer require atk4/data
composer g require psy/psysh:@stable  # optional, but handy for debugging!

Define your first model class:

namespace my;
class User extends \Atk4\Data\Model
{
    public $table = 'user';
    function init(): void
    {
        parent::init();

        $this->addFields(['email','name','password']);
        // use your table fields here
    }
}

Next create console.php:

<?php
include'vendor/autoload.php';
$db = \Atk4\Data\Persistence::connect(PDO_DSN, USER, PASS);
eval(\Psy\sh());

Finally, run console.php:

$ php console.php

Now you can explore. Try typing:

> $m = new \my\User($db);
> $m->loadBy('email', 'example@example.com')
> $m->get()
> $m->export(['email','name'])
> $m->action('count')
> $m->action('count')->getOne()

Agile Core and DSQL

Agile Data relies on DSQL - Query Builder for SQL persistence and multi-record operations though Actions. Various interfaces and PHP patterns are implemented through Agile Core. For more information use the following links:

UI for Agile Data

In a universe with hundreds of different PHP Crud implementations, we thought you might like to have an open-source Grid/Crud/Forms/Other UI library that is specifically designed for Agile Data.

Please consider our other MIT-licensed project - Agile UI to build something like this:

image

Community and Support

Gitter Stack Overlfow Community Discord User forum

Comments
  • AppScopeTrait no longer working in model

    AppScopeTrait no longer working in model

    Wit recent develop branch compared to 2.3.1, AppScopeTrait does not work any longer with model but creates an error, see picture.

    The previously working setup is as follows:

    1. in MyApp::_construct I created a new $this->db = atk4\myapp\PersistenceSql which is just an extension of Persistence\Sql with "use AppScopeTrait;". I then do $this->db->setApp($this);
    2. In my own model atk4\myapp\Model before I just needed to add "use AppScopeTrait;"

    Error see pictureimage

    opened by mkrecek234 25
  • [epic] Implement support for Oracle

    [epic] Implement support for Oracle

    Agile Data should be able to work with Oracle out of the box. But there are some issues and problems which DSQL (https://github.com/atk4/dsql) should work-around:

    • [x] CRUD is not working on Oracle (link related issue here)
    • [x] Disable ` for field escaping
    • [x] Work-around LIMIT support with rownum and subquery.
    • [ ] No automated test-suite

    If you require Agile Data Oracle support - Please help.

    opened by romaninsh 25
  • Standard types overhaul

    Standard types overhaul

    The current fields types are not sufficiently flexible and not contain enough information and options for field use cases.

    After #307 is done I look to break down "Field" class into multiple ones focus on various parameters where user can define types.

    The definition would remain as before, however based on type, presence of "enum" and "values" properties an appropriate field class would be used, so making this possible:

    $model->addField('age', ['type'=>'integer', 'min'=>14]);
    $model->addField('height', ['type'=>'length', 'units'=>['m','cm']]);
    

    Here are some of the use-cases that new implementation should support:

    Field\Boolean:

    • contain yes/no values for the database (1/0)
    • contain yes/no values for the UI (yes/)

    Field\List:

    • supporting enum, values and hard-coded lists
    • add ability to specify colors

    Field\Date:

    • add fields for default UI formatting
    • support date, datetime and time types
    • support min/max dates

    Field\Number:

    • specify how many decimal numbers
    • is negative permitted
    • formatting style (e.g. xx-xxxx)
    • generic prefix and postfix
    • support types 'money', 'percent', 'decimal', 'float', 'rating'
    • allow min/max

    Field\Measurement:

    • support values that are expressed in units
    • duration: s, m, d, w
    • weight: g, kg
    • length: mm, cm, m, km
    • imperial units
    • set to (1cm) stored as (100).
    • automatically reformats for ui
    • types: 'duration', 'length'

    PLEASE SUGGEST MORE TYPES IN THE COMMENTS.

    question Refactoring 
    opened by romaninsh 22
  • Isolate ArrayAccess from Model to optional/BC trait

    Isolate ArrayAccess from Model to optional/BC trait

    ->get('x'), ->set('x', 'v') are fully enough, originally also very unclear when to use when.

    Also misleading as model is iterable on rows, not values (like you would expect when you iterate over an array).

    For BC trait can be used or even the composer data installation can be patched to add this trait to Model by default.

    This PR is big, but solely to isolate the ArrayAccess to make it optional, there is actually not additional different except presence/non-presence of the ArrayAccess impl. on Model.

    Cleanup BC-break 
    opened by mvorisek 21
  • Drop Persistence\Array_ in favor of Sqlite in memory (full SQL) backend

    Drop Persistence\Array_ in favor of Sqlite in memory (full SQL) backend

    if benchmark of Sqlite with memory backend can show, that Sqlite in memory database is fast enought, I propose to drop array/hash table backend completely because:

    • limited functionality vs Sql
    • much easier repo maintanance
    • [ ] sql table created from model

    possible disadvantages:

    • [ ] performance

    /cc @DarkSide666 @georgehristov please provide feedback

    enhancement Cleanup 
    opened by mvorisek 16
  • Add Model checkpoint functions

    Add Model checkpoint functions

    I developed the checkpoint ability to address an issue with the UserAction validateBeforeExecute method, which uses the getDirtyRef method to determine if something has changed in the model in a field where it shouldn't have changed. The problem is that things other than the UserAction could have made those changes. For example, consider:

    class Dummy extends Model
    {
        public $table = 'dummy';
        
        protected function init(): void
        {
            parent::init();
            $this->addField('name');
            $this->addField('foo', ['never_persist' => true]);
            
            $this->onHook(Model::HOOK_AFTER_LOAD, function($m) {
               $m->set('foo', 'crash'); 
            });
        }
    }
    

    using a standard CRUD edit or even delete UserAction will cause an exception, since validateBeforeExecute will detect the change in the 'foo' field.

    The new checkpoint and getDirtyVsCheckpoint methods allow the validateBeforeExecute method to detect only the changes since the checkpoint was made, which by default is after the record is entirely loaded -- after the HOOK_AFTER_LOAD hook has been executed. The methods are general enough to be used in other circumstances as well, and support an arbitrary number of checkpoints.

    opened by samsouthardjr 15
  • Revert join test fix after join/reference clone is fixed

    Revert join test fix after join/reference clone is fixed

    introduced/discovered in https://github.com/atk4/data/pull/697

    I think, there is a bug in Join/Reference when model is cloned. I belive, that clone should be equivalent and should work exactly the same.

    bug BC-break 
    opened by mvorisek 14
  • Persistence\Array_ not allowing zero id, throwing error

    Persistence\Array_ not allowing zero id, throwing error

    1. Create a model from an array with $m = new Model(new Array_($t)); where $t= [ ['name' => 'January', 'sales' => 20000, 'purchases' => 10000], ['name' => 'February', 'sales' => 23000, 'purchases' => 12000]]

    2. Iterate through model with foreach ($model as $row) { }

    Atk4\Data throws an error on iteration as it does not accept id to be zero in \Atk4\Data\Field Line 311.

    $value = (int) $value; if ($this->required && empty($value)) { throw new ValidationException([$this->name => 'Must not be a zero'], $this->getOwner()); }

    @mvorisek Probably due to stricter type validation, but that way Persistence\Array_ is broken.

    waitingForReaction 
    opened by mkrecek234 13
  • Reverse join not saving

    Reverse join not saving

    Following situation:

    Table 1 (contact_table): contact_id (primary), tbl1_name, tbl1_address

    Table_2 (additional_info_table): additional_id (primary), additional_contact_id (relation), additional_content

    Let's assume I have a contact table which is loaded always (Table 1). I want to read, if there's an entry available from an additional table (Table 2). Table 2 always references to Table 1.

    class myModel extends \atk4\data\Model {
        use \atk4\core\TrackableTrait;
        use \atk4\core\AppScopeTrait;
    
        public $table = "contact_table";
        public $id_field = "contact_id";
        
        function init(): void {
            parent::init();
    
            $this->leftJoin($this->'additional_info_table', [
              'master_field' => 'contact_id',
              'foreign_field' => 'additional_contact_id',
              'reverse' => true
            ]);
        }
    }
    

    When using this model in a form, it reads properly, but it just saves data from the main table. The data of the joined table is neither inserted nor updated. Is this how it's intended to work? If yes, then how to deal with a situation like this?

    bug 
    opened by bedengler 13
  • ContainsMany does not fire ref model hook when set or save

    ContainsMany does not fire ref model hook when set or save

    Consider code below:

    class Client extends \Atk4\Data\Model
    {
        public $table = 'client';
        public $caption = 'Client';
    
        protected function init(): void
        {
            parent::init();
    
            $this->addField('name');
            $this->containsMany('Accounts', ['model' => [Account::class]]);
        }
    }
    
    class Account extends \Atk4\Data\Model
    {
        public $caption = ' ';
    
        protected function init(): void
        {
            parent::init();
    
            $this->addField('email', [
                'required' => true,
                'ui' => ['multiline' => [Multiline::INPUT => ['icon' => 'envelope', 'type' => 'email']]],
            ]);
        }
    }
    

    When changing Accounts reference value, then the Account model hook should be fire. Actually id does not.

    Ex:

    $m = new Client($app->db);
    // loading client that has 3 related account record.
    $clientEntity = $m->load(6);
    
    $ac = $clientEntity->get('Accounts');
    array_pop($ac);
    $clientEntity->set('Accounts', $ac);
    $clientEntity->save();
    

    Then, when setting Account or saving Client, the Account model hook should be fired.

    enhancement 
    opened by ibelar 12
  • Add Session persistence

    Add Session persistence

    Implements simple session persistence which will store model data in local session.

    P.S. @mvorisek it will then be used in https://github.com/atk4/ui/blob/3fd37f0bee0d43f01e9eccb41e96b9dc3466d5ca/src/TableColumn/FilterModel/Generic.php#L15 to get rid of sessionTrait and $model->name usage there.

    in review 
    opened by DarkSide666 11
  • Default UserAction 'delete' should have a confirmation message

    Default UserAction 'delete' should have a confirmation message

    Steps to reproduce:

    • Click on a delete button in any Crud
    • Record will be deleted immediately

    Expected behaviour: By default, there should be a confirmation required before deletion

    So adding 'confirmation' = 'Do you really want to delete this record?'here

    https://github.com/atk4/data/blob/448e3085699c9a1b2591a777129b4f9bbf317704/src/Model/UserActionsTrait.php#L135

    enhancement 
    opened by mkrecek234 1
  • Assert data are unchanged during save

    Assert data are unchanged during save

    Use case: You have code which updates the total sum of an order. Now the customer adds an order position with price = 0. You don't want to deal with it but just save the new total (which is identical to the old total).

    Steps to reproduce:

    1. You have any model $model with an entity id=1, total_amount =100
    2. You do `$model->load(1)->save(['total_amount' => 100]);

    You will get this error in recent develop branch: Atk4\Data\Exception: Update failed, exactly 1 row was expected to be affected

    I don't think this is an exception - testing for changes is done in save command - if not change happens because re-save of prior set values, then it is not an Exception. This behaviour was added with the recent changes in Atk4/Data

    enhancement 
    opened by mkrecek234 11
  • Allow to get target model after deep traversal

    Allow to get target model after deep traversal

    class Order extends \Atk4\Data\Model {
     protected function init(): void
        {  
            
            parent::init();
            $this->hasMany('order_doc', ['model' => [OrderDoc::class
            ]]);
            
        }
    }
    
    
    class OrderDoc extends \Atk4\Data\Model { 
    
        protected function init(): void
        { 
            parent::init();
        
            $this->hasOne('order_id', ['model' => [Order::class]]);
            
            $this->onHook(\Atk4\Data\Model::HOOK_BEFORE_INSERT, function ($model, &$data) {
            
                    // Create order if not existing
                    if (!$model->get('order_id') && $model->get('customer_id')) {
                    
                        $model->set('order_id', $model->refLink('order_id')->insert([...]);
                        ]));   }
            
            });
        }
    }
    
    // Use case 1: Create a parent order container, when a new child order doc is created
    $newOrderDoc = (new OrderDoc($db))->createEntity();
    $newOrderDoc->save([...]);
    
    // Use case 2: Assign an order document to another order
    $order = (new Order($db))->load(1);
    $orderdoc = $order->ref('order_doc')->load(1);
    
    $orderdoc->saveAndUnload(['order_id' => 2]);
    

    When traversing on null value, we may allow the ID to change (to newly inserted). I will check what can be done and if it will not break other tests.

    use case 2 - how should this work? Previously, the save conditions were relaxed, to me, incorrectly. What is the reasoning to relax save conditions when the entity cannot be loaded after?

    A solution I can think of would be to implement Model::getModelWithoutRefConditions(). Currently, we do not store traversed reference info, so in $orderdoc, we have no info which condition to remove. Let's discuss this feature request later. If you have (or can put) your project into Github, I can code some helper methods specific to your projects.

    opened by mvorisek 3
  • Aggregate of non-physical field is broken

    Aggregate of non-physical field is broken

    discovered in https://github.com/atk4/data/commit/2f93908c6d77a969712e635148931997b1fa8a81#diff-bff9bb1a3b20aa2d2fb5c6aa8c3a31d22dcbba69bdebc2ec936bb0b45d479febR74

    all tests must pass even if reloadAfterSave is enabled (default)

    bug 
    opened by mvorisek 0
Releases(4.0.0)
  • 4.0.0(Nov 26, 2022)

    Major features

    • Check model conditions during insert/update/delete (#1044) @mvorisek
    • Add Migrator methods to create DB foreign keys (#1019) @mvorisek
    • Replace Model::persistence property with getter/setter (#948) @mvorisek
    • Model::tryLoad returns null when not found (#996) @mvorisek
    • Add Model\AggregateModel model (#817) @georgehristov
    • Add support for Model nesting (#946) @mvorisek
    • Add support for native MySQL driver (mysqli) (#952) @mvorisek

    Breaking changes

    • Remove Field::toString() method (#1074) @mvorisek
    • Remove Field::getTypeObject() method (#1072) @mvorisek
    • Rename all remaining properties to camelCase (#1043) @mvorisek
    • Make SQL Expression & Query classes abstract (#1048) @mvorisek
    • Rename Field negated mandatory to nullable (#1041) @mvorisek
    • Rename Field never_persist, never_save, read_only properties to camelCase (#1040) @mvorisek
    • Rename Model::getRef() to getReference() (#1034) @mvorisek
    • Rename less frequently used properties to camelCase (#1030) @mvorisek
    • Remove WITH/CTE column mapping for model (#1021) @mvorisek
    • Remove "<>", "is", "is not", "not" operators support (#1013) @mvorisek
    • Improve datetime types typecasting (#1009) @mvorisek
    • Distinguish between executeStatement and executeQuery (#965) @mvorisek
    • Throw when foreign record cannot be found (#1001) @mvorisek
    • Assert no traversing on unloaded entity field (#969) @mvorisek
    • Adjust renamed "short_name" to "shortName" (#979) @mvorisek
    • Stricter Model::{addExpression, addCalculatedField} seed (#973) @mvorisek
    • Drop Oracle 11g support (#964) @mvorisek
    • Drop DBAL 2.x support (#963) @mvorisek
    • Remove Model\ArrayAccessTrait trait (#958) @mvorisek
    • All Query methods excl. execute() should be read only (#953) @mvorisek
    • Persistence::action() should be read only (#949) @mvorisek
    • Clone user actions for entity lazily (#943) @mvorisek

    Other changes

    • Unify CI and fix phpstan v1.9.0 (#1075) @mvorisek
    • Upgrade DBAL to v3.4.x (#1069) @mvorisek
    • Fix phpstan v1.8.9 (#1071) @mvorisek
    • Model::addUserAction should accept seed /w class (#1070) @mvorisek
    • Adjust tests to the latest phpunit (#1068) @mvorisek
    • Test PHP 8.2 /wo composer.json platform override (#1066) @mvorisek
    • Remove Model::dirtyAfterReload internal property (#1064) @mvorisek
    • Fix phpstan iterable types (#1062) @mvorisek
    • Fix 1061 PR for atk4/ui (#1063) @mvorisek
    • Fix phpstan v1.8.3 (#1061) @mvorisek
    • Enable phpstan bleading edge and strict rules (#1059) @mvorisek
    • Minor CS cleanup (#1057) @mvorisek
    • Minor CS fixes (#1056) @mvorisek
    • Minor CS cleanup (#1055) @mvorisek
    • Minor tests and CS refactoring (#1050) @mvorisek
    • Rename all methods to camelCase (#1049) @mvorisek
    • Fix ui seed merge in HasOneSql (#1047) @mvorisek
    • Fix model reload after save (#1042) @mvorisek
    • Fix HasOneSql imported field update support (#1038) @mvorisek
    • Fix HasOneSql their field update hook (#1037) @mvorisek
    • Improve string type with NL normalization (#1036) @mvorisek
    • Test FK violation during FK create (#1035) @mvorisek
    • Connect to test DB lazily from TestCase (#1032) @mvorisek
    • Use fixed PHP CS Fixer v3.9.4 (#1031) @mvorisek
    • Add custom DBAL driver middleware (#1029) @mvorisek
    • Fix DBAL Sqlite foreign key 5427, 5485, 5497 and 5501 issues (#1028) @mvorisek
    • Add Migrator::drop support to drop linked FKs first (#1027) @mvorisek
    • Add Schema\TestCase::getConnection() method (#1026) @mvorisek
    • Fix SqlitePlatform to support quoted table name (#1025) @mvorisek
    • Fix table /w schema support for all DBs (#1024) @mvorisek
    • Do not use broken PHP CS Fixer v3.9.1 (#1023) @mvorisek
    • Fix join with aliased fields (#1020) @mvorisek
    • Add SQL string literal escape method (#1017) @mvorisek
    • Fix whole query regexes performance (#1018) @mvorisek
    • Fix PostgreSQL LIKE operator support (#1014) @mvorisek
    • Replace SQL persistence connection property with getter (#1016) @mvorisek
    • Remove Model::withId() method (#1015) @mvorisek
    • Fix Oracle limit 0 support (#1012) @mvorisek
    • Fix Model::loadXxx() /w limit/offset set (#1011) @mvorisek
    • Add Model::executeCountQuery() method (#1008) @mvorisek
    • Add testing with PHP 8.2 (#981) @mvorisek
    • Forbid ContainsXxx unmanaged data modification (#1004) @mvorisek
    • Upgrade hintable to v1.9 (#1007) @mvorisek
    • Fix/test char types length limits (#1006) @mvorisek
    • Improve Model load hooks (#1005) @mvorisek
    • Strip schema from table for Join::foreign_field guess (#1003) @mvorisek
    • Strip schema from table for HasMany::their_name guess (#1002) @samsouthardjr
    • Fix ContainsOne model/entity hook (#999) @mvorisek
    • CS and type improvements (#995) @mvorisek
    • Fix exception throwing for MSSQL insert queries (#993) @mvorisek
    • Fix 64-bit IEEE 754 floating-point precision support (#991) @mvorisek
    • Improve MSSQL/Oracle missing fields in group fix (#988) @mvorisek
    • Add support for AggregateModel traversing (#987) @mvorisek
    • Fix hasXxx on AggregateModel (#986) @mvorisek
    • Fix MoneyType::requiresSQLCommentHint() return type (#983) @mvorisek
    • Remove strpos usage where possible (#982) @mvorisek
    • Fix docs CS (#980) @mvorisek
    • Test pdo_oci NCLOB read fix in image-php in CI (#978) @mvorisek
    • Fix long string param support for Oracle (#974) @mvorisek
    • Fix phpstan v1.4.7 (#972) @DarkSide666
    • Connection DSN can omit password (#971) @DarkSide666
    • Fix Oracle Instant Client setup for CI (#970) @mvorisek
    • Fix leaking PHP iterator (#966) @mvorisek
    • Fix self-signed cert for MSSQL CI (#968) @mvorisek
    • Use Model for Join write operations (#960) @mvorisek
    • Add support for Model nesting for Array persistence (#961) @mvorisek
    • Test with MySQL Server 5.6 and fix unordered export/2D assertions (#956) @mvorisek
    • Add support for native Oracle driver (OCI8) (#954) @mvorisek
    • Fix phpstan for DBAL 3.2.1 (#951) @mvorisek
    • Allow Model::getUserActions() to be called only on model (#947) @mvorisek
    Source code(tar.gz)
    Source code(zip)
  • 3.1.0(Dec 29, 2021)

    What’s Changed

    • Fix field name key source, it must use always short_name (#942) @mvorisek
    • Make sure Reference::createTheirModel always returns a new instance (#941) @mvorisek
    • Model only properties should be settable only from non-entity (#940) @mvorisek
    • Add WITH support for MSSQL (#935) @mvorisek
    • Do not dump query twice on SQL exception (#928) @mvorisek
    • Add generics to EntityFieldPair for Phpstan (#927) @mvorisek
    • Fix 4000 bytes CLOB limit for Oracle (#922) @mvorisek
    • Fix MSSQL with DBAL 2.x (#923) @mvorisek
    • Store binary values natively for PostgreSQL and MSSQL (#921) @mvorisek
    • Fix Migration::dropIfExists for Oracle (#920) @mvorisek
    • Add binary/blob column support for MSSQL and Oracle (#917) @mvorisek
    • Do not return the same object in {Model, Persistence}::add methods (#916) @mvorisek
    • Normalize ID in array persistence and fix type guessing in static persistence (#911) @mvorisek
    • Upgrade phpstan to v1.0 (#910) @mvorisek
    • Make DBAL character types case-insensitive on PostgreSQL & Oracle (#908) @mvorisek
    • Unify CI files (#906) @mvorisek
    • Upgrade hintable to v1.6 (#905) @mvorisek
    • Require field to be present when typecasting a row (#898) @mvorisek
    • Fix Join last inserted ID source (#777) @mvorisek
    • Do not reuse Oracle connections for CI (#903) @mvorisek
    • Fix Oracle insert AI inconsistency (#778) @mvorisek
    • Fix issues with Oracle server (#901) @mvorisek
    • Adjust to CS fixer v3.2 rules (#900) @mvorisek
    • Test with PHP 8.1 (#888) @mvorisek
    • Fix PR 891 (#894) @mvorisek
    • Fix compatibility with PHP 8.1 (#890) @mvorisek
    • Deduplicate Field properties (#891) @mvorisek
    • HasOneSql::addField(): Use type from reference Model (#880) @PhilippGrashoff
    • Zero memory leak testing (#883) @mvorisek
    • Test with limited max. connections (#878) @mvorisek
    • Test with MariaDB (#882) @mvorisek
    • Bump versions for the next release (#877) @mvorisek

    Breaking Changes

    • Accept DBAL driver connection in Connection::connect (#939) @mvorisek
    • Expression::render must not mutate the state (#938) @mvorisek
    • Deprecate Model::loaded in favor of Model::isLoaded (#931) @mvorisek
    • Deprecate Model::onlyFields in favor of Model::setOnlyFields (#930) @mvorisek
    • Add Password Field, drop multiple support in Email Field (#924) @mvorisek
    • Deduplicate Reference from entity (#915) @mvorisek
    • Unify common methods (#914) @mvorisek
    • Fix Field mandatory & normalize in row save typecast (#899) @mvorisek
    • Fix field typecast and drop Boolean field class (#897) @mvorisek
    • Rename "money" type to "atk4_money" type (#895) @mvorisek
    • Drop dummy "password" type (#896) @mvorisek
    • Move Atk4\Dsql under Atk4\Data\Persistence\Sql namespace (#879) @mvorisek

    Major Features

    • Deduplicate model properties and Field/Join from entity (#912) @mvorisek
    • Use DBAL Type for Field type (#727) @mvorisek
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(May 11, 2021)

    What’s Changed

    Major Breaking Changes

    • Drop PHP v7.3 support and upgrade CS Fixer to v3 (#872) @mvorisek
    • Load methods return a new instance (distinguish strictly between model and entity) (#862) @mvorisek

    Breaking Changes

    • Upgrade phpstan to level 6 (#871) @mvorisek
    • Fix Field clone with Reference and do not mutate model scope in Condition::toWords (#865) @mvorisek
    • Make Model::{data,dirty,scope} props private (#864) @mvorisek
    • Fix traversal when parent model is loaded (#853) @mvorisek
    • Fix next AI for array persistence (#859) @mvorisek
    • No iterate over entity (#855) @mvorisek
    • Remove Model::{asModel, saveAs, newInstance} methods (#856) @mvorisek

    Other Changes

    • Integrate atk4/dsql package (#873) @mvorisek
    • Fix PostgreSQL boolean handling for PHP 8.0.5 (#870) @mvorisek
    • Use assertCount where applicable (#863) @mvorisek
    • Fix "field" action - return all records, allow Action as scope condition (#861) @mvorisek
    • Normalize array persistence seed data (#710) @mvorisek
    • Fix too long trigger name for Oracle autoincrement (#858) @mvorisek
    • Test schema with MSSQL, Oracle and PostgreSQL (#857) @mvorisek
    • No explicit $table param in Array & Csv persistences (#656) @mvorisek
    • Fix anonymous class detection (#854) @mvorisek
    Source code(tar.gz)
    Source code(zip)
  • 2.4.0(Apr 16, 2021)

    What’s Changed

    • Update release builder (#850) @mvorisek
    • Use RefMany, strict checks relaxed in upstream (#852) @mvorisek
    • Update hintable dep to 1.2.x (#851) @mvorisek
    • Use strict tryLoadOne in ContainsOne (#848) @mvorisek
    • Make Persistence class abstract (#847) @mvorisek
    • Remove ui property from UserAction (#843) @mvorisek
    • PHPStan issue 4267 fixed officially (#840) @mvorisek
    • Better num key det (#839) @mvorisek
    • Improve table_alias gen (#836) @mvorisek
    • Feature/fix conditions (#838) @DarkSide666
    • Add model in ValidationExceptions (#837) @DarkSide666
    • Fix title field name guessing (#835) @mvorisek
    • Add additional Join test with field actual names and prefixes (#834) @DarkSide666
    • Reenable MSSQL testing (#754) @mvorisek
    • Nice caption with debug {actual, hintable} prefix (#831) @mvorisek
    • Check hasMany guess early (#830) @mvorisek
    • Adjust for latest = 8.0 php version (#828) @mvorisek
    • Simplify phpstan CI patch (#825) @mvorisek
    • Fix composer autoload/PSR4 warning (#824) @mvorisek
    • Convert hardcoded strings to hintable fields in Contains{One, Many}Test (#822) @mvorisek
    • Fix PHPStan 4267 issue until fixed officially (#821) @mvorisek
    • Better table phpdoc (#818) @mvorisek
    • Adjust Expressionable::getDsqlExpression() prototype (#816) @mvorisek
    • Fix PHPStan Model IteratorAggregate phpdoc (#808) @mvorisek
    • Rename Persistence\Array_::get() method to getRows() (#813) @mvorisek
    • Cleanup for DBAL 2.10 and higher (#807) @mvorisek
    • Remove Model\Base class introduced temporary in PR 800 (hintable support) (#810) @mvorisek
    • Use short Model name everywhere (#805) @mvorisek

    Breaking Changes

    • Require array in reference/join defaults (#820) @mvorisek
    • Table must be specified with table key in defaults (#819) @mvorisek
    • Improve PHPStan level to 5 (#814) @mvorisek
    • [fix] Model::duplicate (#801) @georgehristov
    • Change HasOne type to integer by default (#811) @mvorisek
    • Implicit ID have default type of integer and is required by default (#806) @mvorisek

    Major Features

    • Deduplicate load methods and assert only one row can be loaded if expected (#846) @mvorisek
    • Add hintable fields support (#800) @mvorisek
    Source code(tar.gz)
    Source code(zip)
  • 2.3.5(Dec 4, 2020)

  • 2.3.2(Dec 4, 2020)

  • 2.3.1(Oct 13, 2020)

  • 2.2.1(Oct 13, 2020)

  • 2.3.0(Oct 6, 2020)

    What’s Changed

    • Fix GC for CallbackFilterIterator using WeakReference (#737) @mvorisek
    • Fix scheduled/200 iterations burn test (#736) @mvorisek
    • Add smoke and burn CI testing (#735) @mvorisek
    • Add loadany() to Array_ Persistence (#715) @PhilippGrashoff
    • Fix array persistence - addIdToLoadRow() must be called on data passed by-reference (#731) @DarkSide666
    • fix Model::addFields() with field object (#730) @mvorisek
    • Better Field::compare() (#729) @mvorisek
    • Persistence\Sql::getFieldSqlExpression return expression using field owner expr method (#722) @georgehristov
    • Introduce Field::getPersistenceName method (#728) @georgehristov
    • Revert to official Docker image (#726) @mvorisek
    • Update CS - require strict comparison (#631) @mvorisek
    • Add Oracle DB support (#723) @mvorisek
    • [fix] Condition key can be Expressionable (#721) @georgehristov
    • Use dsql\Query::exists (#725) @georgehristov
    • Add MSSQL support (#719) @mvorisek
    • Fix issues with dirty ID (#718) @mvorisek
    • Fix PostgreSQL CI errors (#706) @mvorisek
    • On-the-fly transform of ID in array persistance from key to result data (#709) @mvorisek
    • Fix missing Model::reload_after_save restore (#703) @mvorisek
    • Add type hinting to persistence connect (#701) @PhilippGrashoff
    • Update tests CS (#698) @mvorisek
    • Add type validation to factory calls (#624) @mvorisek
    • Move types to code if possible (#554) @mvorisek
    • [refactor] move ModelArrayAccessTrait to correct place in NS structure (#694) @georgehristov
    • Remove no longer relevant comment (#693) @mvorisek
    • Fix query on hasOne referenced model (#686) @georgehristov
    • Refactor usages of call_user_func to direct calls (#687) @mvorisek
    • Move Join to Model\Join (#680) @georgehristov
    • Introduce getQueryArguments in Field (#685) @georgehristov
    • Reference::addField $ourFieldName variable name for clarity (#681) @georgehristov
    • Separate Model reference methods in a trait (#679) @georgehristov
    • Improve naming consistency renaming Model\HasUserActionsTrait to Model\UserActionsTrait (#678) @georgehristov
    • Refactor references NS to use consistent and explicit variable and method names (#675) @georgehristov
    • Improve Model::withPersistence (#676) @georgehristov
    • Use constants to define Condition operators (#673) @georgehristov
    • Fix typo in phpdoc in Scope class (#674) @mvorisek
    • Мove Scope class to Model NS (#672) @georgehristov
    • Rename CompoundCondition class to Scope (#671) @georgehristov
    • Drop legacy/hack support of OR junction in anything else than legacy Model::addCondition() (#670) @mvorisek
    • Persistence\Csv rename variables to explicit names (#668) @georgehristov
    • Scope improve usability - addCondition() should not wrap (#664) @mvorisek
    • Rename BasicCondition to Condition (#663) @mvorisek
    • Unify Model::addCondition() and CompoundCondition::addCondition() (#666) @georgehristov
    • Persistence\Sql rename variables to explicit names (#665) @georgehristov
    • Fix Scope::createXx bug (#667) @mvorisek
    • Fix Iterator data types (#659) @DarkSide666
    • Fix composer.json (#658) @DarkSide666

    Breaking Changes

    • Fix not rebound hooks after clone (#711) @mvorisek
    • Remove persistence override support from Model::load/save/atomic methods (#732) @mvorisek
    • Stricter Condition validation, remove legacy OR condition support (#720) @mvorisek
    • Drop magic ID property from Model (#716) @mvorisek
    • Drop $from_persistence support for Model::load() (#717) @mvorisek
    • Drop ID property in advance of standard row data, provide BC using magic (#708) @mvorisek
    • Convert Model to Entity (prevent reload with a different record) (#697) @mvorisek
    • Make init() methods protected and fix calls to ->invokeInit() (#695) @mvorisek
    • Use "asc"/"desc" strings for order direction instead of false/true (#626) @mvorisek

    Major Features

    • Fix not rebound hooks after clone (#711) @mvorisek
    • Drop magic ID property from Model (#716) @mvorisek
    • Convert Model to Entity (prevent reload with a different record) (#697) @mvorisek
    • Use Scope class for filtering/where impl. for Model (#660) @georgehristov
    Source code(tar.gz)
    Source code(zip)
  • 2.2.0(Jul 15, 2020)

    Breaking Changes

    • Enforce array in method prototypes when appropriate (#567) @mvorisek
    • Add separate/explicit Model::setMulti() method, Model::set() no longer accepts array (#621) @mvorisek
    • Drop AppScopeTrait trait from Model & Persistence classes (#654) @mvorisek
    • Fix PSR class/trait/method naming (#652) @mvorisek
    • [refactor] UserAction\Generic to Model\UserAction (#634) @georgehristov
    • Improve array persistence and disallow passing array by reference (#627) @mvorisek
    • HasXxx() methods now return bool only + always require field to be defined first (#616) @mvorisek

    What’s Changed

    • Fix composer develop install issue (#657) @DarkSide666
    • Update CS for PHP 7.3 (#653) @mvorisek
    • Drop PHP 7.2 support (#649) @mvorisek
    • Separate UserAction validation in a protected method (#650) @georgehristov
    • Move safe "@return bool" phpdoc to code (#647) @mvorisek
    • Fix UserAction\Generic deprecation warning (#646) @georgehristov
    • Test with native MySQL 8.0 (#645) @mvorisek
    • Add testing with PHP 8.0 (#644) @mvorisek
    • [hotfix] backward compatibility for UserAction::$scope property (#643) @georgehristov
    • fix reference (#642) @DarkSide666
    • Fix default action icon seeds (#640) @mvorisek
    • Use strict array seed (#639) @mvorisek
    • [fix] dsql Postresql Connection class (#638) @georgehristov
    • [update] use dsql refactored connection class (#637) @georgehristov
    • Fix generated caption for anonymous classes (#633) @mvorisek
    • Html param for DSQL\Expression::getDebugQuery() is no longer supported (#632) @mvorisek
    • Update CS - require strict types (#625) @mvorisek
    • Fix tests when SqlFormatter formatter lib is available (#628) @mvorisek
    • Add codecov.yml config (#623) @mvorisek
    • Better fix for null offset limit (#613) @mvorisek
    • Remove @throws by CS fixer (#622) @mvorisek
    • Fix typo - ModelArrayAccessTrait is trait (not class) (#620) @mvorisek
    • Fix tests - exception message changed in atk4/core (#619) @mvorisek
    • Delete CHANGELOG.md (#618) @mvorisek
    • Revert mandatory validation (keep unfixed as before phpunit upgrade) (#615) @DarkSide666
    • Fix Email field test (#612) @DarkSide666
    • Upgrade phpunit (#561) @mvorisek
    Source code(tar.gz)
    Source code(zip)
  • 2.1.0(Jun 7, 2020)

    Large Breaking Changes

    • Isolate ArrayAccess from Model to optional/BC trait (#591) @mvorisek - add ModelArrayAccessTrait to your Model classes where needed
    • Change and move hook names to constants (#597) @mvorisek - read and update your code. Version 2.0.5 should be backwards compatible with this.

    Breaking Changes

    • Convert callables to closures and move hook names to constants (#597) @mvorisek
    • Remove expr explode (#609) @mvorisek
    • Remove support of default field + do not explode CSV string/value (#604) @mvorisek
    • Allow to add() only an object (#565) @mvorisek
    • Remove NameTrait trait from Model (#596) @mvorisek

    What’s Changed

    • Fix exception constructor refactoring (#608) @mvorisek
    • Refactor exception constructor calls to not use array (#607) @mvorisek
    • Remove support of relative class names (#603) @mvorisek
    • Small normalization changes (#580) @mvorisek
    • Remove lastInsertID() from Model (#592) @mvorisek
    • [feature] Action modifier (#602) @ibelar
    • add documentation for Model::setNull() (#601) @romaninsh
    • Remove NameTrait trait from Model (#594) @mvorisek
    • hotfix ArrayAccess (#595) @mvorisek
    • No incomplete tests by default for Sqlite and MySQL (#583) @mvorisek
    • move lastInsertID to persistence (#585) @DarkSide666
    • Normalize Unit Testing workflow across repos (#571) @mvorisek
    • Update Model.php (#584) @vorismi3
    • Model::setNull() - Allow field value unset (#569) @mvorisek
    • Remove old Travis files (#550) @mvorisek
    • add missing double backslash in class references (#570) @georgehristov
    • Simplify locale path getter (#568) @mvorisek
    • Delete ConnectionInterface.php (#564) @mvorisek
    • Fix phpunit before full upgrade (#562) @mvorisek
    • Fix driver type refactor (#559) @mvorisek
    • Remove vim comment (#560) @mvorisek
    • Change "driverType" to "driver type" in exception message (#558) @mvorisek
    • Don't use addMethod() with non-callable. Also add return types (#556) @mvorisek
    • Remove deprecated classes in 2.x version (#552) @mvorisek
    • Refactor driver type (#553) @mvorisek
    Source code(tar.gz)
    Source code(zip)
  • 2.0.4(Jun 7, 2020)

  • 2.0.3(Apr 8, 2020)

    What’s Changed

    • Fix refactorability for "owner" (#543) @mvorisek
    • Use null coalescing operator instead of isset (#545) @mvorisek
    • Convert scalar class names to ::class (#544) @mvorisek
    • introduce sql null condition test (#540) @georgehristov
    • Fix value trim (#538) @DarkSide666
    • fix #533 (#537) @DarkSide666
    • Fix LIKE conditions - fix fix (#535) @mvorisek
    • chore: merge develop into feature/number-field (#528) @georgehristov
    • Fix LIKE conditions for Array and SQL persistences (#532) @DarkSide666
    • Action should also support fields===true value (#531) @DarkSide666
    • Implement addWith() (#527) @DarkSide666
    • Fix hook trait usage (#525) @mvorisek
    • Simplify code (#519) @mvorisek
    • Do not fail-fast PHP test matrix (#522) @mvorisek
    • Callback fields should not be sortable (#516) @DarkSide666
    • [REFACTOR] rename addHook to onHook (#514) @georgehristov
    • New method $model->getTitles() (#513) @DarkSide666
    • Fix hasOne relation seed processing. It should replace not merge. (#512) @DarkSide666
    Source code(tar.gz)
    Source code(zip)
  • 2.0.2(Feb 11, 2020)

    What’s Changed

    • include comment about hooks and example how to execute them (#510) @georgehristov
    • Add microseconds persistence support for datetime/time types + fix normalization/cloning issue (#504) @mvorisek
    • Accept any DateTimeInterface impl. for datetime (#505) @mvorisek
    • update multiple delete example in docs (#503) @georgehristov
    • Fix/ Small Action related fix (#502) @ibelar
    • better implementation for addFields() and test cases (#499) @DarkSide666
    • Fix empty array condition (#498) @DarkSide666
    • model default add field property (#454) @funyx
    • Feature/set custom edit exec button (#490) @georgehristov
    • Feature/add confirmation callback argument (#491) @georgehristov
    • Feature remove specified action (#489) @georgehristov
    • Fix/persistence sql condition (#487) @georgehristov
    • Fix/delete callback (#488) @georgehristov
    Source code(tar.gz)
    Source code(zip)
  • 2.0.1(Dec 2, 2019)

    What’s Changed

    • clean up bundler (#493) @romaninsh
    • Feature/pgsql test (#486) @DarkSide666
    • Implement #386 (#477) @DarkSide666
    • added locale test (#485) @romaninsh

    Enhancements

    • Add matrix testing (#492) @romaninsh
    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Nov 25, 2019)

    What’s Changed

    • Feature/test1 (#483) @romaninsh
    • Add pgsql workflow (#480) @romaninsh
    • Implement GitHub action support (#478) @romaninsh
    • Fix model OR condition typecast (#476) @DarkSide666
    • EXPEREMENTAL - use Actions internally (#442) @romaninsh
    • setOrder() support for expressions (#475) @DarkSide666
    • Reference caption implementation (#470) @DarkSide666
    • Lokalise: Translations update (#468) @romaninsh
    • Implements onRollback hook (#466) @DarkSide666
    • Lokalise: Translations update (#464) @romaninsh
    • Add Unit test cases for field default value (#458) @abbadon1334
    • add is_update flag on beforeSave Hook! (#462) @romaninsh
    • containsOne/Many changes for UI (#459) @DarkSide666
    • Only call \DateTime::format if its an instance (#453) @PhilippGrashoff
    • Reference get caption (#447) @abbadon1334
    • Fix Email Field Validation - Add Require ext-intl (#448) @abbadon1334
    • Added new Email type (#446) @romaninsh
    • Model::addField() to use $model->fields property rather than elements (and new Trait) (#445) @romaninsh
    • Feature/actions use multicontainer (#444) @romaninsh
    • Add support, specifying which fields can be dirty during action call. (#440) @romaninsh
    • Apply fixes from StyleCI (#441) @romaninsh
    • Apply fixes from StyleCI (#437) @DarkSide666
    • CSV persistence should sanitize headers / field names (#436) @DarkSide666
    • Implements User Actions as per wiki (#399) @romaninsh
    • Improving documentation of Model (#419) @romaninsh
    • Implements DeepCopy->transformData() (#430) @DarkSide666
    • add condition if ref(hasOne) model not loaded (#413) @funyx
    • Nesty BUG. was still returning wrong (#431) @gowrav-vishwakarma
    • Improve wording in persistence.rst (#427) @acicovic
    • Fix typo in joins.rst (#426) @acicovic
    • Apply fixes from StyleCI (#425) @romaninsh
    • should solve bug #698 (#414) @gowrav-vishwakarma
    • implement action(field) for array persistence, fix #415 (#421) @DarkSide666
    • Feature/contains one many 2 (#412) @DarkSide666
    • Apply fixes from StyleCI (#1) @funyx
    • Apply fixes from StyleCI (#411) @DarkSide666
    • Feature/persistence array add iterator method like (#406) @funyx
    • Feature/fix 409 loadby (#410) @DarkSide666
    • Refactor namespaces (#405) @DarkSide666
    • Fix logic error in example (#397) @PhilippGrashoff
    • Dark side666 patch 1 (#398) @romaninsh
    • Apply fixes from StyleCI (#395) @DarkSide666
    • Fix #389 Field Time after save wrong return value (#394) @abbadon1334
    • implement insert lookups (#392) @romaninsh
    Source code(tar.gz)
    Source code(zip)
  • 1.4.1(Feb 23, 2019)

    Fixed bugs:

    • Check if actual and other seed parameters work fine in join #387
    • \atk4\data\Field_Callback does not exist #363

    Closed issues:

    • Migrate Model AutoCreator to Agile Data #251
    • Write article on how to store data encrypted #143

    Merged pull requests:

    • fix #387 #388 (@DarkSide666)
    • Fix problem with addExpression on models with Persistence_Array #385 (@skondakov)
    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Feb 4, 2019)

    • Add implementation for sorting, ordering and actions in Persistence_Array.
    • afterSave hook gains 2nd argument ($is_update)
    • New pattern: implemented of SubTypes
    • Model::hasField() added for convenience (similar to hasElement())
    • New aggregation function - concat
    • Implemented "DeepCopy" feature (experemental)
    • Implemented insert lookups (https://medium.com/@romaninsh/atk-data-and-data-import-ingestion-82bdb81c74dd)
    • Refactor ['type'=>'boolean'] into Boolean class #314 (experemental)

    Fixed bugs:

    • Model: $this->set($this->id_field, $this->id) after insert #365
    • Issue with afterSave, AfterInsert and AfterUpdate Hooks together with hasOne()->addField() #383
    • our_field is honored in Reference impl, but on save a validation error is thrown #370
    • hasMany on Join in not implemented yet #369
    • Copy with reference traversal #360
    • Model::ref() does not apply condition on hasMany Reference if Model is not loaded #355
    • Escaped quote issue #349
    • $model->each() should return array of returned values. #348
    • Documentation mistake #347
    • Looks like type=>'text' is possible but not documented #338
    • Persistence_CSV and Persistence_Array does not support export() #312

    Merged pull requests:

    • Feature/add action in aftersave hook #384 (@DarkSide666)
    • Feature/normalize date values #381 (@DarkSide666)
    • Feature/fix newinstance #380 (@DarkSide666)
    • Support callables like [$this,'func'] for addRef() #378 (@DarkSide666)
    • implement Array persistence traversal, conditions and actions #377 (@romaninsh)
    • Implement export() for Array and CSV persistences #374 (@DarkSide666)
    • Fix Reference_SQL_One lookup bugs, adds tests #373 (@DarkSide666)
    • Fix Join->hasOne/hasMany #372 (@DarkSide666)
    • Apply fixes from StyleCI #371 (@DarkSide666)
    • Save id value after insert to id_field #368 (@romaninsh)
    • Feature/extend setorder even more #367 (@DarkSide666)
    • Fix typo and setOrder() with array args #364 (@mvorisek)
    • Implement SubType support (overriding class) #361 (@romaninsh)
    • Implementation of Deep Copy feature #359 (@romaninsh)
    • Feature/add test for 355 #357 (@DarkSide666)
    • beforeInsert/beforeUpdate Note #354 (@PhilippGrashoff)
    • Feature/line end normalize #353 (@DarkSide666)
    • Feature/json error #351 (@DarkSide666)
    • Implement Lookups for inserts #350 (@romaninsh)
    • Apply fixes from StyleCI #346 (@romaninsh)
    • Feature/json unserialize error check (compatible with php 7.3) #345 (@romaninsh)
    • Apply fixes from StyleCI #344 (@romaninsh)
    • Apply fixes from StyleCI #343 (@romaninsh)
    • Apply fixes from StyleCI #342 (@romaninsh)
    • Feature/type text #339 (@DarkSide666)
    Source code(tar.gz)
    Source code(zip)
  • 1.3.7(Aug 16, 2018)

  • 1.3.6(Jul 5, 2018)

    This release adds some great improvements to the aggregate fields. The most notable change is that you no longer need to specify 'field' for 'count' aggregation, it has been a thorn:

    $this->hasMany('Invoices', new Invoice())->addField('invoice_count', ['aggregate'=>'count']);
    

    Now the alternative syntax is available, where you can just define your expression explicitly:

    $this->hasMany('Items', new Item())->addField('list', ['expr'=>'group_concat([name] separator "+")']);
    

    Closed issues:

    • Documentation atk4/data not meet atk/data-primer repository #328

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 1.3.3(Apr 3, 2018)

    Fixed bugs:

    • DSN without password #298
    • adding reference multiple times does not produce error #239
    • addTitle() doesn't work for fields without _id suffix #220
    • looks like misspelled ->table #212

    Closed issues:

    • Model->export: using ID as first level array key? #311
    • Refactor to use SQLTestCase from atk4/core #258
    • Docs: There is wrong description and examples #204
    • action('field') on expression should alias it to name of field. #190
    • Typecast use of 'actual' clashes with persistence->update() #186

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 1.3.2(Apr 2, 2018)

  • 1.3.1(Mar 2, 2018)

    Closed issues:

    • Typecasting for id field #293
    • Should action('xyz')->execute() trigger hooks? #291

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 1.3.0(Feb 3, 2018)

    Add support for Oracle and PostgreSQL, adding support for sequences and migrate to ATK Data 1.2.x

    Fixed bugs:

    • Tests of CSV persistence fail on Windows #271

    Closed issues:

    • Problem with oracle insert #280
    • Oracle dates not working properly #279
    • After latest composer update, warning is thrown in Persistence_SQL->initQueryFields() #274
    • [epic] Implement support for Oracle #270

    Merged pull requests:

    Source code(tar.gz)
    Source code(zip)
  • 1.2.3(Jan 2, 2018)

    This version focuses on enabling you to define your own Field classes, such as more advanced Password handling field. See example: https://github.com/atk4/login/blob/master/src/Field/Password.php

    Introduced new way to verify field values with $model->compare('age', 20); which returs boolean. Also in addition to enum property, you can use values property with a field now.

    Rewrote Overview section of documentation and added new information on Fields and Static Persistence.

    • Added Persistence_Static #265
    • Implemented $field->values property #266
    • Added Field->compare() and Model->compare()
    • Improved support for user-defined fields (nee Field\Password from https://github.com/atk4/login) #259
    • Allow to specify join kind
    • Allow fields extended from Field class to be loaded from SQL #269
    • Started official support of 7.2.
    • Fixed typecasting when using Array persistence
    • Extra docs on: Fields, Static Persistence
    • Docs rewrite of Overiew section.
    Source code(tar.gz)
    Source code(zip)
  • 1.2.2(Dec 3, 2017)

    1.2.2

    Agile Data was created some time ago, before factory() implementation was completed in Agile Core. At that time we had to make a decision how to better set the default class for the field, so we used propreties such as _default_class_addField. Now that Agile Core allows us to specify Seed, we have refactored some of that internal functionality to rely on factory(). This should not impact your applications unless you are using custom persistence. #261

    Additional fixes:

    • Persistence to hold Model Prefix #152
    • adding addTitle() now hides 'id' field #252 #253
    • refLink() to pass $defaults #254
    • couldn't connect to sqlite due to ;charset postfix in DSN #256 #257
    • improve iterating models without id field #260
    Source code(tar.gz)
    Source code(zip)
  • 1.2.1(Sep 12, 2017)

Pure PHP NoSQL database with no dependency. Flat file, JSON based document database.

Please give it a Star if you like the project ?? ❤️ SleekDB - A NoSQL Database made using PHP Full documentation: https://sleekdb.github.io/ SleekDB i

Kazi Mehedi Hasan 745 Jan 7, 2023
SleekwareDB is a NoSQL database storage service. A database storage service that can be used for various platforms and is easy to integrate.

SleekwareDB is a NoSQL database storage service. A database storage service that can be used for various platforms and is easy to integrate. NoSQL API

SleekwareDB 12 Dec 11, 2022
SQL database access through PDO.

Aura.Sql Provides an extension to the native PDO along with a profiler and connection locator. Because ExtendedPdo is an extension of the native PDO,

Aura for PHP 533 Dec 30, 2022
phpMyFAQ - Open Source FAQ web application for PHP and MySQL, PostgreSQL and other databases

phpMyFAQ 3.1 What is phpMyFAQ? phpMyFAQ is a multilingual, completely database-driven FAQ-system. It supports various databases to store all data, PHP

Thorsten Rinne 547 Dec 27, 2022
A Symfony application for managing and automating regular backups of MySQL databases.

DbSaver DbSaver is an application written by Bastien LOUGHIN allowing you to make automatic daily backups (and manual backups) for your MySQL database

Bastien 35 Nov 11, 2022
You can sync any number of PDO supported databases

Features: Can backup any number of databases. No need to introduce column name only table name The Last id based data backup Support 12 different data

Tharusha Kavishan Udumulla 4 Aug 27, 2021
The Enobrev\ORM library is a small framework of classes meant to be used for simply mapping a mysql database to PHP classes, and for creating simply SQL statements using those classes.

The Enobrev\ORM library is a small framework of classes meant to be used for simply mapping a mysql database to PHP classes, and for creating simply SQL statements using those classes.

Mark Armendariz 0 Jan 7, 2022
Independent query builders for MySQL, PostgreSQL, SQLite, and Microsoft SQL Server.

Aura.SqlQuery Provides query builders for MySQL, Postgres, SQLite, and Microsoft SQL Server. These builders are independent of any particular database

Aura for PHP 424 Dec 12, 2022
A validating SQL lexer and parser with a focus on MySQL dialect.

SQL Parser A validating SQL lexer and parser with a focus on MySQL dialect. Code status Installation Please use Composer to install: composer require

phpMyAdmin 368 Dec 27, 2022
A SQL query builder with zero dependencies

Latitude Query Builder A SQL query builder with zero dependencies. Attempts to be PSR-1, PSR-2, and PSR-4 compliant. Install composer require latitude

Woody Gilk 618 Dec 30, 2022
A php securised login system, using Hash, Salt and prevent from SQL Injections

A Basic Secure Login Implementation Hashed & Salted password ( only hashed in ShA-512 for now ) No SQL injection possible Prevent XSS attacks from the

Yohann Boniface 1 Mar 6, 2022
SQL to Laravel Query Builder

Marwan - SQL To Laravel Builder SQL to Laravel Query Builder, A Converter written in PHP Features Converts SQL Queries to Laravel Query Builder. Assis

Rexhep Shijaku 162 Dec 19, 2022
Extract SQL statements from migrations

This is my package MigrationToSql To install: composer require bcleverly/migrationtosql --dev This repo is here to help you extract the SQL queries fr

Ben 4 Apr 15, 2022
A minimalistic implementation of asynchronous SQL for PHP.

libSQL A minimalistic implementation of asynchronous SQL for PHP. Installation via DEVirion Install the DEVirion plugin and start your server. This wi

null 10 Dec 7, 2022
Tiny php mysql lib (PDO-based) with handy fetch/update functionality, supports both SQL and parametric queries

Micro PHP mysql lib (~ 200 lines of code) with ultra powerful CRUD for faster than ever development: parametric fetch/insert/update/delete (based on a

Mr Crypster 18 Dec 10, 2022
A Laravel package to output a specific sql to your favourite debugging tool. The supported log output is Laravel Telescope, Laravel Log, Ray, Clockwork, Laravel Debugbar and your browser.

Laravel showsql A Laravel package to output a specific sql to your favourite debugging tool, your browser or your log file. Use case You often want to

Dieter Coopman 196 Dec 28, 2022
API abstracting communication with SQL providers (eg: MySQL) on top of PDO inspired by Java JDBC

SQL Data Access API Table of contents: About Configuration Execution Installation Unit Tests Examples Reference Guide About This API is a ultra light

Lucian Gabriel Popescu 0 Jan 9, 2022
Propel2 is an open-source high-performance Object-Relational Mapping (ORM) for modern PHP

Propel2 Propel2 is an open-source Object-Relational Mapping (ORM) for PHP. Requirements Propel uses the following Symfony Components: Config Console F

Propel 1.2k Dec 27, 2022
High performance distributed database for mysql

High performance distributed database for mysql, define shardings by velocity&groovy scripts, can be expanded nodes flexible...

brucexx 33 Dec 13, 2022