{"id":15908015,"url":"https://github.com/atk4/data","last_synced_at":"2025-05-14T16:11:52.919Z","repository":{"id":7981631,"uuid":"56442737","full_name":"atk4/data","owner":"atk4","description":"Data Access PHP Framework for SQL \u0026 high-latency databases","archived":false,"fork":false,"pushed_at":"2025-04-07T12:09:30.000Z","size":10701,"stargazers_count":275,"open_issues_count":27,"forks_count":47,"subscribers_count":16,"default_branch":"develop","last_synced_at":"2025-04-14T22:08:03.307Z","etag":null,"topics":["agile","atk4","nosql","orm","persistence","php","sql"],"latest_commit_sha":null,"homepage":"https://atk4-data.readthedocs.io/en/stable/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atk4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-17T15:09:18.000Z","updated_at":"2025-04-07T12:09:33.000Z","dependencies_parsed_at":"2024-01-27T15:29:35.330Z","dependency_job_id":"3e214df0-b072-4ca1-8b2d-cedc2b7acc31","html_url":"https://github.com/atk4/data","commit_stats":{"total_commits":1891,"total_committers":26,"mean_commits":72.73076923076923,"dds":0.6435748281332628,"last_synced_commit":"9998be701f07dbfa2deec3884dcd844058bba0bc"},"previous_names":[],"tags_count":67,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Fdata","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Fdata/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Fdata/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Fdata/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atk4","download_url":"https://codeload.github.com/atk4/data/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248968914,"owners_count":21191162,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agile","atk4","nosql","orm","persistence","php","sql"],"created_at":"2024-10-06T14:08:51.867Z","updated_at":"2025-05-14T16:11:52.909Z","avatar_url":"https://github.com/atk4.png","language":"PHP","readme":"# ATK Data - Data Model Abstraction for Agile Toolkit\n\n[Agile Toolkit](https://atk4.org/) 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.\n\nAgile Data is a framework for defining your \"business layer\" which is separate from your \"presentation layer\" and \"persistence\". Together with [Agile UI](https://github.com/atk4/ui) you can deliver user interface \"out of the box\" or with [Agile API](https://github.com/atk4/api) - general-purpose API endpoints.\n\n- Agile Data uses PHP to define your Business Objects, their properties and actions.\n- Agile Data works with SQL, NoSQL or external API sources.\n- Agile Data plugs into generic UI components (Crud, Card, Table, Form, etc) with a minimum code.\n- Agile Data supports \"user actions\". UI layer uses \"action executor\" to read ACL-controlled metadata.\n- Agile Data is developer-friendly and easy to learn\n- Agile Data is high-performance, capable of abstracting aggregation logic and shifting it into a capable database persistence (such as SQL) through advanced expressions.\n- Agile Data is extensible - field types, persistence types, relations and action types can be extended.\n\n[![Build](https://github.com/atk4/data/actions/workflows/test-unit.yml/badge.svg?branch=develop)](https://github.com/atk4/data/actions?query=branch:develop)\n[![CodeCov](https://codecov.io/gh/atk4/data/branch/develop/graph/badge.svg)](https://codecov.io/gh/atk4/data)\n[![GitHub release](https://img.shields.io/github/release/atk4/data.svg)](https://github.com/atk4/data/releases)\n[![Code Climate](https://codeclimate.com/github/atk4/data/badges/gpa.svg)](https://codeclimate.com/github/atk4/data)\n\nQuick-Links: [Documentation](https://atk4-data.readthedocs.io) | [Discord channel](https://discord.gg/QVKSk2B) | [ATK UI](https://github.com/atk4/ui)\n\n## Is ATK Data similar to ORM?\n\nYes and no.\n\nAgile 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:\n\n```php\n$vipClientModel = (new Client($db))-\u003eaddCondition('is_vip', true);\n\n// express total for all VIP client invoices. The value of the variable is an object\n$totalDueModel = $vipClientModel-\u003eref('Invoice')-\u003eaction('fx', ['sum', 'total']);\n\n// single database query is executed here, but not before!\necho $totalDueModel-\u003egetOne();\n```\n\nIn other ORM the similar implementation would be either [slow, clumsy, limited or flawed](https://medium.com/@romaninsh/pragmatic-approach-to-reinventing-orm-d9e1bdc336e3).\n\n## How ATK Data integrates with UI (or API)\n\nAgile Toolkit is a low-code framework. Once you have defined your business object, it can be associated with a UI widget:\n\n```php\nCrud::addTo($app)-\u003esetModel(new Client($db), ['name', 'surname'], ['edit', 'archive']);\n```\n\nor with an API end-point:\n\n```php\n$api-\u003erest('/clients', new Client($db));\n```\n\n## Extensibility and Add-ons\n\nATK Data is extensible and offers wide range of add-ons like [Audit](https://github.com/atk4/audit).\n\nRegardless 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](https://github.com/atk4/chart).\n\n### Benefits of using ATK Data\n\nDesigned for medium to large PHP applications and frameworks, ATK Data is a clean implementation of Data Mapper that will:\n\n- Make your application really database-agnostic. SQL? NoSQL? RestAPI? Cache? Load and store your data with any of these, without refactoring your code.\n- 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.\n- 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.\n- Extensions. \"[Audit](https://github.com/atk4/audit)\" - transparently record all edits, updates and deletes with \"Undo\" support.\n- [Out of the box UI](https://github.com/atk4/ui). Who wants to build Admin systems today? Tens of professional components: [Crud](https://ui.atk4.org/demos/collection/crud.php), [Grid](https://ui.atk4.org/demos/collection/grid.php), [Form](https://ui.atk4.org/demos/form/form.php) as well as add-ons like [Charts](https://github.com/atk4/chart) can be added to your PHP app with 3-lines of code.\n- RestAPI server for Agile Data is currently under development.\n- Agile Data and all extensions mentioned above are licensed under MIT and are free to use.\n\nSince the initial introduction of Agile Data back in [2016](https://www.reddit.com/r/PHP/comments/5ftpxg/thank_you_reddit_you_helped_me_create_something/) our group of early-adopters used it in large production PHP projects. **It is time for you to try Agile Data today**.\n\n### Getting Started\n\nWatch [Quick Start](https://atk4-data.readthedocs.io/en/develop/quickstart.html) or [Screencasts](https://www.youtube.com/watch?v=o16xwkFfnuA\u0026t=182s\u0026index=1\u0026list=PLUUKFD-IBZWaaN_CnQuSP0iwWeHJxPXKS). There is also [Full Documentation](https://atk4-data.readthedocs.io/) ([PDF](https://atk4-data.readthedocs.io/_/downloads/en/develop/pdf/)).\n\nATK Data relies on ATK Core and can be greatly complimented by ATK UI:\n\n- [Agile Core](https://atk4-core.readthedocs.io/) - documents various low-level traits and features such as Containers, Hooks or Exceptions ([PDF](https://atk4-core.readthedocs.io/_/downloads/en/develop/pdf/))\n- [Agile UI](https://atk4-ui.readthedocs.io/) - documents optional UI components and how to build Web App with them. ([PDF](https://atk4-ui.readthedocs.io/_/downloads/en/develop/pdf/))\n\n## When to use Agile Data?\n\nWe 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.\n\n### HTML Applications and Admin Systems\n\nMost 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.\n\n![integration-orm](docs/images/integration/integration-orm.png)\n\nAs 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.\n\n\u003e *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.*\n\nAgile Data addresses this balance. For the presentation logic you can use tools such as [Agile UI](https://github.com/atk4/ui), that consists of generic Crud, Form implementations or other modules which accept the Model protocol of Agile Data:\n\n```php\n$presentation-\u003esetModel($businessModel);\n```\n\nThis 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).\n\n![integration-atk](docs/images/integration/integration-atk.png)\n\nIt's important to note, that glue may also interact with the model preparing it for a specific use-case:\n\n```php\n$grid = new \\Atk4\\Ui\\Table();\n$data = new Order($db);\n$data-\u003eaddCondition('is_new', true);\n$data-\u003eaddCondition('client_id', $_GET['client_id']);\n$grid-\u003esetModel($data);\n\n$html = $grid-\u003erender();\n```\n\n### Domain-Model Reports\n\nObject 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.\n\nAgile Data was designed in a way where all of your code can rely ONLY on model objects. This includes the reports.\n\nThis next example builds a complex \"Job Profitability Report\" by only relying on Model logic:\n\n```php\nclass JobReport extends Job\n{\n    protected function init(): void\n    {\n        parent::init();\n\n        // Invoice contains Lines that may relevant to this job\n        $invoice = new Invoice($this-\u003egetPersistence());\n\n        // we need to ignore draft invoices\n        $invoice-\u003eaddCondition('status', '!=', 'draft');\n\n        // build relation between job and invoice line\n        $this-\u003ehasMany('InvoiceLines', ['model' =\u003e static function () use ($invoice) {\n            return $invoice-\u003eref('Lines');\n        }])\n            -\u003eaddField('invoiced', [\n                'aggregate' =\u003e 'sum',\n                'field' =\u003e 'total',\n                'type' =\u003e 'atk4_money'\n            ]);\n\n        // build relation between Job and Timesheets\n        $this-\u003ehasMany('Timesheets', ['model' =\u003e static function (Persistence $persistence) {\n            // next we need to see how much is reported through timesheets\n            $timesheet = new Timesheet($persistence);\n\n            // timesheet relates to client, import client.hourly_rate as expression\n            $timesheet-\u003egetReference('client_id')-\u003eaddField('hourly_rate');\n\n            // calculate timesheet cost expression\n            $timesheet-\u003eaddExpression('cost', ['expr' =\u003e '[hours] * [hourly_rate]']);\n\n            return $timesheet;\n        }])\n            -\u003eaddField('reported', [\n                'aggregate' =\u003e 'sum',\n                'field' =\u003e 'cost',\n                'type' =\u003e 'atk4_money'\n            ]);\n\n        // finally lets calculate profit\n        $this-\u003eaddExpression('profit', ['expr' =\u003e '[invoiced] - [reported]']);\n\n        // profit margin could be also useful\n        $this-\u003eaddExpression('profit_margin', ['expr' =\u003e 'coalesce([profit] / [invoiced], 0)']);\n    }\n}\n```\n\nYour Report Model:\n\n- moves query logic to the database (SQL)\n- is still a model, so compatible with all UI Components and extensions\n\nIn order to output results on HTML table:\n\n```php\n$grid = new \\Atk4\\Ui\\Grid();\n$data = new JobReport($db);\n$grid-\u003esetModel($data);\n\n$html = $grid-\u003erender();\n```\n\nOr if you want to display them as a Chart using https://github.com/atk4/chart\n\n```php\n$chart = new \\Atk4\\Chart\\BarChart();\n$data = new JobReport($db);\n\n// BarChart wants aggregated data\n$data-\u003eaddExpression('month', ['expr' =\u003e 'month([date])']);\n$aggregate = new AggregateModel($data);\n$aggregate-\u003esetGroupBy(['month'], [\n    'profit_margin' =\u003e ['expr' =\u003e 'sum'],\n]);\n\n// associate presentation with data\n$chart-\u003esetModel($aggregate, ['month', 'profit_margin']);\n$html = $chart-\u003ehtml();\n```\n\nIn both cases you end up executing **just one** SQL query.\n\n### Large Application and Enterprise use\n\n#### Refactoring\n\nOne 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:\n\n\u003e 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.\n\nAgile Data gives you all the tools to do this in a few steps:\n\n- Update your Model definition by replacing \"expression\" with a regular field.\n- Create a \"migrator\" script which calculates expression using [action](https://atk4-data.readthedocs.io/en/develop/quickstart.html#actions).\n- Change model behaviors adding Model Hook (afterSave) to re-calculate \"profit\" within same ACID transaction.\n\nThis will not break the rest of your applications - UI, RestAPI or Reports will continue to work, but faster.\n\n#### Audit and Customization\n\nI explain some basic customization in the video: https://www.youtube.com/watch?v=s0Vh_WWtfEs\u0026index=5\u0026list=PLUUKFD-IBZWaaN_CnQuSP0iwWeHJxPXKS\n\nThere is also \"Advanced Topics\" section in the documentation: https://atk4-data.readthedocs.io/en/develop/advanced.html\n\n#### Multi-System Applications\n\nMost 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.\n\nAgile 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.\n\n#### Migrating from one Database to Another and cross-persistence\n\nWith 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.\n\nAs usual - the rest of your application is not affected and you can even use multiple types of different persistencies and still navigate through references.\n\n#### Support\n\nBecause our team have implemented Agile Data, we have trained experts who can offer commercial consultancy, training and support. Use our Contact form: https://www.agiletoolkit.org/contact for inquiries.\n\n## Framework Integrations\n\nAgile Data (and in some cases Agile UI) have been integrated by community with other popular frameworks:\n\n- Laravel: https://github.com/atk4/laravel-ad\n- Wordpress: https://github.com/ibelar/atk-wordpress\n- More integrations wanted!\n\n## Q\u0026A\n\n#### Q: I noticed that Agile Data uses sub-selects instead of JOIN. I believe JOIN is more efficient.\n\nWhile 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.\n\nYou can, however, [import fields through joins too](https://atk4-data.readthedocs.io/en/develop/joins.html)\n\n#### Q: I don't like the `$book-\u003eset('field', 123)`, I prefer properties\n\nAgile 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-\u003egetId()`, `$model-\u003egetPersistence()` and `model-\u003egetDataRef()`.\n\nRead more on [working with individual data records](https://atk4-data.readthedocs.io/en/develop/persistence.html).\n\n#### Q: I do not like to use class `\\Atk4\\Data\\Model` as a parent\n\nClass `Model` implements a lot of essential functionality. If you need a deeper explanation read my blog post: https://www.agiletoolkit.org/blog/why-should-you-extend-your-entity-class\n\n#### Q: Agile Data has small community\n\nThis 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.\n\nAgile Data is still relatively new framework and it takes time until PHP community recognizes it.\n\n#### Q: There is broken link / documentation / page\n\nWe 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.\n\n#### Q: Is there training material for Agile Data / Agile UI\n\nWe are working on it. For now - visit our [![Discord](https://img.shields.io/badge/discord-User_Forum-green.svg)](https://discord.gg/QVKSk2B).\n\n#### Q: How can I help / Contribute?\n\nSay hi. We enjoy meeting new people regardless of how good they are with PHP and the framework [![Discord](https://img.shields.io/badge/discord-User_Forum-green.svg)](https://discord.gg/QVKSk2B).\n\nIf you want to help, we have a special tag [Help Wanted](https://github.com/atk4/data/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) in our issue system:\n\n-----------------\n\nsome of the information below may be out of date and needs to be cleaned up.\n\n## Agile Data at a Glance\n\nAgile 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.\n\nYou 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:\n\n```php\n$m = new Client($db);\necho $m-\u003eaddCondition('vip', true)\n    -\u003eref('Order')-\u003eref('Line')-\u003eaction('fx', ['sum', 'total'])-\u003egetOne();\n```\n\nResulting Query will always use parametric variables if vendor driver supports them (such as PDO):\n\n```sql\nselect sum(`price`*`qty`) from `order_line` `O_L` where `order_id` in (\n    select `id` from `order` `O` where `client_id` in (\n        select `id` from `client` where `vip` = :a\n    )\n)\n\n// :a is \"Y\"\n```\n\nAgile 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.\n\nAll 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.\n\nMy next example demonstrates how simple and clean your code looks when you store new Order data:\n\n```php\n$m = new Client($db);\n$m-\u003eloadBy('name', 'Pear Company');\n$m-\u003eref('Order')\n    -\u003esave(['ref' =\u003e 'TBL1', 'delivery' =\u003e new DateTime('+1 month')])\n    -\u003eref('Lines')-\u003eimport([\n        ['Table', 'category' =\u003e 'furniture', 'qty' =\u003e 2, 'price' =\u003e 10.5],\n        ['Chair', 'category' =\u003e 'furniture', 'qty' =\u003e 10, 'price' =\u003e 3.25],\n    ]);\n```\n\nResulting queries (I have removed back-ticks and parametric variables for readability) use a consise syntax and demonstrate some of the \"behind-the-scenes\" logic:\n\n- New order must belong to the Company. Also company must not be soft-deleted.\n- `delivery` is stored in field `delivery_date`, also the DateTime type is mapped into SQL-friendly date.\n- `order_id` is automatically used with Lines.\n- `category_id` can be looked up directly inside the INSERT (standard feature of SQL reference fields).\n\n```sql\nselect id, name from client where name = 'Pear Company' and is_deleted = 0;\ninsert into order (company_id, ref, delivery_date)\n    values (293, 'TBL1', '2015-18-12');\ninsert into order_lines (order_id, title, category_id, qty, price) values\n    (201, 'Table', (select id from category where name = 'furniture'), 2, 10.5),\n    (201, 'Chair', (select id from category where name = 'furniture'), 19, 3.25);\n```\n\nIf you have enjoyed those examples and would like to try them yourself, continue to https://github.com/atk4/data-primer.\n\n### Introducing Models\n\nAgile Data uses vendor-independent and lightweight `Model` class to describe your business entities:\n\n```php\nclass Client extends \\Atk4\\Data\\Model\n{\n    public $table = 'client';\n\n    protected function init(): void\n    {\n        parent::init();\n\n        $this-\u003eaddField('name');\n        $this-\u003eaddField('address');\n\n        $this-\u003ehasMany('Project', ['model' =\u003e [Project::class]]);\n    }\n}\n```\n\n- Documentation: https://atk4-data.readthedocs.io/en/develop/model.html\n- Examples: https://github.com/atk4/data-primer/tree/master/src\n\n### Introducing Actions\n\n ![mapping](docs/images/mapping.png)\n\nAnything 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.\n\nEach persistence implements actions differently. SQL is probably the most full-featured one:\n\n![GitHub release](docs/images/action.gif)\n\n- Documentation: https://atk4-data.readthedocs.io/en/develop/quickstart.html?highlight=action#actions\n\n### Introducing Expressions\n\nSmart Fields in Agile Toolkit are represented as objects. Because of inheritance, Fields can be quite diverse at what they do. For example `SqlExpressionField` can define field through custom SQL or PHP code:\n\n![GitHub release](docs/images/expression.gif)\n\n- Documentation: https://atk4-data.readthedocs.io/en/develop/expressions.html\n\n### Introducing References\n\nForeign keys and Relation are bread and butter of RDBMS. While it makes sense in \"Persistence\", not all databases support Relations.\n\nAgile Data takes a different approach by introducing \"References\". It enables you to define relationships between Domain Models that can work with non-relational databases, while allowing you to perform various operations such as importing or aggregating fields. (use of JOIN is explained below)\n\n![GitHub release](docs/images/import-field.gif)\n\n- Documentation: https://atk4-data.readthedocs.io/en/develop/references.html\n\n### Model Conditions and DataSets\n\nConditions (or scopes) are rare and optional features across ORMs but they are one of the most significant features in Agile Data. It allows you to create objects that represent multiple database records without actually loading them.\n\nOnce a condition is defined, it will appear in actions and will also restrict you from adding non-compliant records.\n\n![GitHub release](docs/images/reference-magic.gif)\n\n- Documentation: https://atk4-data.readthedocs.io/en/develop/conditions.html\n\n### Build Reports inside Domain Model\n\nWith 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 the unique features of your DataBase while letting you stay inside Domain Model.\n\nHow 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:\n\n![GitHub release](docs/images/domain-model-reports.gif)\n\nDid you notice that the query has automatically excluded canceled projects?\n\n### Model-level join\n\nMost ORMs can define models that only work with a single SQL table. If you have\nto store logical entity data into multiple tables - tough luck, you'll have to do\nsome linking yourself.\n\nAgile Data allows you to define multiple joins right inside your model. As you join()\nanother table, you will be able to import fields from the joined table. If you\ncreate a new record, data will automatically be distributed into the tables and\nrecords will be linked up correctly.\n\n![GitHub release](docs/images/model-join.gif)\n\nThe best part about joins is that you can add them to your existing model for specific queries. Some extensions can even do that.\n\n- Documentation: https://atk4-data.readthedocs.io/en/develop/joins.html\n\n### Deep Model Traversal\n\nProbably one of the best feature of Agile Data is deep traversal. Remember how\nyour ORM tried to implement various many-to-many relationships? This is no longer\na problem in Agile Data.\n\nSuppose you want to look at all the countries that have 2-letter name. How many\nprojects are there from the clients that are located in a country with 2-letter name?\n\nAgile Data can answer with a query or with a result.\n\n![GitHub release](docs/images/deep-traversal.gif)\n\n- Documentation: https://atk4-data.readthedocs.io/en/develop/references.html#traversing-dataset\n\n## Advanced Features and Extensions\n\nThe examples you saw so far are only a small fragment of the possibilities you can\nachieve with Agile Data. You now have a new playground where you can design your\nbusiness logic around the very powerful concepts.\n\nOne of the virtues we value the most in Agile Data is ability to abstract and\nadd higher level features on our solid foundation.\n\n### Explorability\n\nIf 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.\n\nWith that, creating a Dynamic Form UI object that automatically includes Dropdown with list of allowed values is possible.\n\nIn fact - we have already stared work on [Agile UI](https://github.com/atk4/ui) project!\n\n### Hooks\n\nYou 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.\n\nOn 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)\nif you need.\n\nAnd guess what - should your model be saved into NoSQL database, the domain-level hooks will be executed, but SQL-specific ones will not.\n\n- Documentation: https://atk4-data.readthedocs.io/en/develop/hooks.html\n\n### Extensions\n\nMost 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.\n\nWe are still working on our Extension library but we plan to include:\n\n- [Audit Log](https://www.agiletoolkit.org/data/extensions/audit) - record all operations in a model (as well as previous field values), offers a reliable Undo functionality.\n- ACL - flexible system to restrict access to certain records, fields or models based on\n  permissions of your logged-in user or custom logic.\n- Filestore - allow you to work with files inside your model. Files are actually\n  stored in S3 (or other) but the references and meta-information remains in the database.\n- Soft-Delete, purge and undelete - several strategies, custom fields, permissions.\n\nMore details on extensions: https://www.agiletoolkit.org/data/extensions\n\n### Performance\n\nIf 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:\n\n```php\nforeach ($client-\u003eref('Project') as $project) {\n    echo $project-\u003eget('name') . \"\\n\";\n}\n\n// $project refers to same object at all times, but $project's active data\n// is re-populated on each iteration\n```\n\nNothing 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.\n\nAgile Data works fast and efficiently even if you have huge amount of records in the database.\n\n### Security\n\nWhen 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.\n\nAgile 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:\n\n```php\n$country-\u003eaddCondition($country-\u003eexpr('length([name]) = []', [$_GET['len']]));\n```\n\nResulting query is:\n\n```php\nwhere length(`name`) = :a\n```\n\nAnother great security feature is invoked when you try and add a new country:\n\n```php\n$country-\u003einsert('Latvia');\n```\n\nThis code will fail, because our earlier condition that \"Latvia\" does not satisfy. This makes variety of other uses safe:\n\n```php\n$client-\u003eload(3);\n$client-\u003eref('Order')-\u003einsert($_POST);\n```\n\nRegardless of what's inside the `$_POST`, the new record will have `client_id = 3` .\n\nFinally, the following is also possible:\n\n```php\n$client-\u003eaddCondition('is_vip');\n$client-\u003eref('Order')-\u003einsert($_POST);\n```\n\nRegardless 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('select')` or `action('fx')` it will only apply to records that match all of the conditions.\n\nThose security measures are there to protect you against human errors. We think that input sanitization is still quite important and you should do that.\n\n## Installing into existing project\n\nStart by installing Agile Data through composer:\n\n```bash\ncomposer require atk4/data\n```\n\nDefine your first model class:\n\n```php\nnamespace my;\n\nclass User extends \\Atk4\\Data\\Model\n{\n    public $table = 'user';\n\n    protected function init(): void\n    {\n        parent::init();\n\n        $this-\u003eaddField('email');\n        $this-\u003eaddField('name');\n        $this-\u003eaddField('password');\n\n        // add your table fields here\n    }\n}\n```\n\nNext create `console.php`:\n\n```php\n\u003c?php\n\nrequire __DIR__ . '/vendor/autoload.php';\n\n$db = \\Atk4\\Data\\Persistence::connect(PDO_DSN, USER, PASS);\neval(\\Psy\\sh());\n```\n\nFinally, run `console.php`:\n\n```\n$ php console.php\n```\n\nNow you can explore. Try typing:\n\n```php\n\u003e $m = new \\my\\User($db);\n\u003e $m-\u003eloadBy('email', 'example@example.com')\n\u003e $m-\u003eget()\n\u003e $m-\u003eexport(['email', 'name'])\n\u003e $m-\u003eexecuteCountQuery()\n```\n\n## Agile Core and DSQL\n\nAgile 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](https://github.com/atk4/core).\n\nHold on! Why yet another query builder? Obviously because existing ones are not good enough. You can write multi-vendor queries in PHP profiting from better security, clean syntax and avoid human errors.\n\nDSQL tries to do things differently:\n\n1. Composability. Unlike other libraries, we render queries recursively allowing many levels of sub-selects.\n2. Small footprint. We don't duplicate query code for all vendors, instead we use clever templating system.\n3. Extensibility. We have 3 different ways to extend DSQL as well as 3rd party vendor driver support.\n4. **Any Query** - any query with any complexity can be expressed through DSQL.\n5. Almost no dependencies. Use DSQL in any PHP application or framework.\n6. NoSQL support. In addition to supporting PDO, DSQL can be extended to deal with SQL-compatible NoSQL servers.\n\nDSQL Is Simple and Powerful\n\n```php\n$query = $connection-\u003edsql();\n$query-\u003etable('employees')\n    -\u003ewhere('birth_date', '1961-05-02')\n    -\u003efield('count(*)');\necho 'Employees born on May 2, 1961: ' . $query-\u003egetOne();\n```\n\nIf the basic query is not fun, how about more complex one?\n\n```php\n// establish a query looking for a maximum salary\n$salary = $connection-\u003edsql();\n\n// create few expression objects\n$eMs = $salary-\u003eexpr('max(salary)');\n$eDf = $salary-\u003eexpr('TimeStampDiff(month, from_date, to_date)');\n\n// configure our basic query\n$salary\n    -\u003etable('salary')\n    -\u003efield(['emp_no', 'max_salary' =\u003e $eMs, 'months' =\u003e $eDf])\n    -\u003egroup('emp_no')\n    -\u003eorder('-max_salary');\n\n// define sub-query for employee \"id\" with certain birth-date\n$employees = $salary-\u003edsql()\n    -\u003etable('employees')\n    -\u003ewhere('birth_date', '1961-05-02')\n    -\u003efield('emp_no');\n\n// use sub-select to condition salaries\n$salary-\u003ewhere('emp_no', $employees);\n\n// join with another table for more data\n$salary\n    -\u003ejoin('employees.emp_id')\n    -\u003efield('employees.first_name');\n\n// finally, fetch result\nforeach ($salary as $row) {\n    echo 'Data: ' . json_encode($row) . \"\\n\";\n}\n```\n\nThis builds and executes a single query that looks like this:\n\n```sql\nSELECT\n    `emp_no`,\n    max(salary) `max_salary`,\n    TimeStampDiff(month, from_date, to_date) `months`\nFROM\n    `salary`\nJOIN\n    `employees` on `employees`.`emp_id` = `salary`.`emp_id`\nWHERE\n    `salary`.`emp_no` in(select `id` from `employees` where `birth_date` = :a)\nGROUP BY `emp_no`\nORDER BY max_salary desc\n\n:a = \"1961-05-02\"\n```\n\n## UI for Agile Data\n\nIn a universe with hundreds of [different PHP Crud implementations](https://codecanyon.net/category/php-scripts?utf8=✓\u0026term=crud\u0026as=0\u0026referrer=search\u0026view=list), we thought you might like to have an open-source Grid/Crud/Forms/Other UI library that is specifically designed for Agile Data.\n\nPlease consider our other MIT-licensed project - [Agile UI](https://atk4.org/) to build something like this:\n\n![image](https://github.com/atk4/ui/raw/develop/docs/images/grid.png)\n\n## Community and Support\n\n[![Stack Overflow Community](https://img.shields.io/stackexchange/stackoverflow/t/atk4.svg)](https://stackoverflow.com/questions/ask?tags=atk4)\n[![Discord Community](https://img.shields.io/badge/discord-User_Forum-green.svg)](https://discord.gg/QVKSk2B)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatk4%2Fdata","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatk4%2Fdata","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatk4%2Fdata/lists"}