{"id":16023970,"url":"https://github.com/symlex/doctrine-active-record","last_synced_at":"2025-04-14T13:22:16.698Z","repository":{"id":57012617,"uuid":"46562026","full_name":"symlex/doctrine-active-record","owner":"symlex","description":"Object-oriented CRUD (create, read, update, delete) for Doctrine DBAL","archived":false,"fork":false,"pushed_at":"2024-07-07T12:19:22.000Z","size":213,"stargazers_count":29,"open_issues_count":1,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-28T02:38:02.863Z","etag":null,"topics":["active-record","dao","doctrine","doctrine-dbal","php"],"latest_commit_sha":null,"homepage":"https://docs.symlex.org","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/symlex.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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":"2015-11-20T12:58:00.000Z","updated_at":"2024-08-07T23:29:15.000Z","dependencies_parsed_at":"2024-12-18T06:46:49.628Z","dependency_job_id":null,"html_url":"https://github.com/symlex/doctrine-active-record","commit_stats":{"total_commits":101,"total_committers":5,"mean_commits":20.2,"dds":"0.27722772277227725","last_synced_commit":"c1804b2225918b98f6cc8eb9ba972aa5582eb6cb"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symlex%2Fdoctrine-active-record","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symlex%2Fdoctrine-active-record/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symlex%2Fdoctrine-active-record/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symlex%2Fdoctrine-active-record/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/symlex","download_url":"https://codeload.github.com/symlex/doctrine-active-record/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248381789,"owners_count":21094528,"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":["active-record","dao","doctrine","doctrine-dbal","php"],"created_at":"2024-10-08T19:04:55.238Z","updated_at":"2025-04-14T13:22:16.663Z","avatar_url":"https://github.com/symlex.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Object-oriented CRUD for Doctrine DBAL\n\n[![Latest Stable Version](https://poser.pugx.org/symlex/doctrine-active-record/v/stable.svg)](https://packagist.org/packages/symlex/doctrine-active-record)\n[![License](https://poser.pugx.org/symlex/doctrine-active-record/license.svg)](https://packagist.org/packages/symlex/doctrine-active-record)\n[![Test Coverage](https://codecov.io/gh/symlex/doctrine-active-record/branch/master/graph/badge.svg)](https://codecov.io/gh/symlex/doctrine-active-record)\n[![Build Status](https://travis-ci.org/symlex/doctrine-active-record.png?branch=master)](https://travis-ci.org/symlex/doctrine-active-record)\n[![Documentation](https://readthedocs.org/projects/symlex-docs/badge/?version=latest\u0026style=flat)](https://docs.symlex.org/en/latest/doctrine-active-record)\n\nAs a lightweight alternative to Doctrine ORM, this battle-tested library provides Business Model and Database Access Object (DAO) classes \nthat encapsulate **Doctrine DBAL** to provide high-performance, object-oriented CRUD (create, read, update, delete) \nfunctionality for relational databases. It is a lot faster and less complex than Datamapper ORM implementations. See [TRADEOFFS.md](TRADEOFFS.md).\n\nDocumentation: https://docs.symlex.org/en/latest/doctrine-active-record/\n\n![Doctrine ActiveRecord](https://docs.symlex.org/en/latest/doctrine-active-record/img/workflow.svg)\n\n## Basic Example ##\n\n```php\n\u003c?php\n\nuse Doctrine\\ActiveRecord\\Dao\\Factory as DaoFactory;\nuse Doctrine\\ActiveRecord\\Model\\Factory;\n\n$daoFactory = new DaoFactory($db); \n\n$modelFactory = new Factory($daoFactory);\n$modelFactory-\u003esetFactoryNamespace('App\\Model');\n$modelFactory-\u003esetFactoryPostfix('Model');\n\n// Returns instance of App\\Model\\UserModel\n$user = $modelFactory-\u003ecreate('User'); \n\n// Throws exception, if not found\n$user-\u003efind(123); \n\nif ($user-\u003eemail == '') {\n    // Update email\n    $user-\u003eupdate(array('email' =\u003e 'user@example.com')); \n}\n\n// Returns instance of App\\Model\\GroupModel\n$group = $user-\u003ecreateModel('Group'); \n```\n\n## Usage in REST Controller Context ##\n\nDoctrine ActiveRecord is perfectly suited for building high-performance REST services.\n\nThis example shows how to work with the EntityModel in a REST controller context. Note, how easy it is to avoid deeply \nnested structures. User model and form factory (provided by the [InputValidation](https://github.com/symlex/input-validation) \npackage) are injected as dependencies.\n\n```php\n\u003c?php\n\nnamespace App\\Controller\\Rest;\n\nuse Symfony\\Component\\HttpFoundation\\Request;\nuse App\\Exception\\FormInvalidException;\nuse App\\Form\\FormFactory;\nuse App\\Model\\User;\n\nclass UsersController\n{\n    protected $user;\n    protected $formFactory;\n\n    public function __construct(User $user, FormFactory $formFactory)\n    {\n        $this-\u003euser = $user;\n        $this-\u003eformFactory = $formFactory;\n    }\n    \n    public function cgetAction(Request $request)\n    {\n        $options = array(\n            'count' =\u003e $request-\u003equery-\u003eget('count', 50),\n            'offset' =\u003e $request-\u003equery-\u003eget('offset', 0)\n        );\n        \n        return $this-\u003euser-\u003esearch(array(), $options);\n    }\n\n    public function getAction($id)\n    {\n        return $this-\u003euser-\u003efind($id)-\u003egetValues();\n    }\n\n    public function deleteAction($id)\n    {\n        return $this-\u003euser-\u003efind($id)-\u003edelete();\n    }\n\n    public function putAction($id, Request $request)\n    {\n        $this-\u003euser-\u003efind($id);\n        \n        $form = $this-\u003eformFactory-\u003ecreate('User\\Edit');\n        \n        $form\n            -\u003esetDefinedWritableValues($request-\u003erequest-\u003eall())\n            -\u003evalidate();\n\n        if($form-\u003ehasErrors()) {\n            throw new FormInvalidException($form-\u003egetFirstError());\n        } \n        \n        $this-\u003euser-\u003eupdate($form-\u003egetValues());\n\n        return $this-\u003euser-\u003egetValues();\n    }\n\n    public function postAction(Request $request)\n    {\n        $form = $this-\u003eformFactory-\u003ecreate('User\\Create');\n        \n        $form\n            -\u003esetDefinedWritableValues($request-\u003erequest-\u003eall())\n            -\u003evalidate();\n\n        if($form-\u003ehasErrors()) {\n            throw new FormInvalidException($form-\u003egetFirstError());\n        }\n        \n        $this-\u003euser-\u003esave($form-\u003egetValues());\n\n        return $this-\u003euser-\u003egetValues();\n    }\n}\n```\n\nSee also: [InputValidation for PHP – Easy \u0026 secure whitelist validation for input data of any origin](https://github.com/symlex/input-validation)\n\n## Data Access Objects ##\n\nDAOs directly deal with **database tables** and **raw SQL**, if needed. `Doctrine\\ActiveRecord\\Dao\\Dao` is suited to implement custom methods using raw SQL. All DAOs expose the following public methods by default:\n- `createDao(string $name)`: Returns a new DAO instance\n- `beginTransaction()`: Start a database transaction\n- `commit()`: Commit a database transaction\n- `rollBack()`: Roll back a database transaction\n\nIn addition, `Doctrine\\ActiveRecord\\Dao\\EntityDao` offers many powerful methods to easily deal with database table rows:\n- `setData(array $data)`: Set raw data (changes can not be detected, e.g. when calling update())\n- `setValues(array $data)`: Set multiple values\n- `setDefinedValues(array $data)`: Set values that exist in the table schema only (slower than setValues())\n- `getValues()`: Returns all values as array\n- `find($id)`: Find a row by primary key\n- `reload()`: Reload row values from database\n- `getValues()`: Returns all values as associative array\n- `exists($id)`: Returns true, if a row with the given primary key exists\n- `save()`: Insert a new row\n- `update()`: Updates changed values in the database\n- `delete()`: Delete entity from database\n- `getId()`: Returns the ID of the currently loaded record (throws exception, if empty)\n- `hasId()`: Returns true, if the DAO instance has an ID assigned (primary key)\n- `setId($id)`: Set primary key\n- `findAll(array $cond = array(), $wrapResult = true)`: Returns all instances that match $cond (use search() or searchAll(), if you want to limit or sort the result set)\n- `search(array $params)`: Returns a `SearchResult` object (see below for supported parameters)\n- `wrapAll(array $rows)`: Create and return a new DAO for each array element\n- `updateRelationTable(string $relationTable, string $primaryKeyName, string $foreignKeyName, array $existing, array $updated)`: Helper function to update n-to-m relationship tables\n- `hasTimestampEnabled()`: Returns true, if this DAO automatically adds timestamps when creating and updating rows\n- `findList(string $colName, string $order = '', string $where = '', string $indexName = '')`: Returns a key/value array (list) of all matching rows\n- `getTableName()`: Returns the name of the underlying database table\n- `getPrimaryKeyName()`: Returns the name of the primary key column (throws an exception, if primary key is an array)\n\n## Search Parameters ##\n\n**search() accepts the following optional parameters to limit, filter and sort search results:**\n\n`table`: Table name\n\n`table_alias`: Alias name for \"table\" (table reference for join and join_left)\n\n`cond`: Search conditions as array (key/value or just values for raw SQL)\n\n`count`: Maximum number of results (integer)\n\n`offset`: Result offset (integer)\n\n`join`: List of joined tables incl join condition e.g. `array(array('u', 'phonenumbers', 'p', 'u.id = p.user_id'))`, see Doctrine DBAL manual\n\n`left_join`: See join\n\n`columns`: List of columns (array)\n\n`order`: Sort order (if not false)\n\n`group`: Group by (if not false)\n\n`wrap`: If false, raw arrays are returned instead of DAO instances\n\n`ids_only`: Return primary key values only\n\n`sql_filter`: Raw SQL filter (WHERE)\n\n`id_filter`: If not empty, limit result to this list of primary key IDs\n\n## Search Result ##\n\nWhen calling `search()` on a `EntityDao` or `EntityModel`, you'll get a `SearchResult` instance as return value.\nIt implements `ArrayAccess`, `Serializable`, `IteratorAggregate` and `Countable` and can be used either as array\nor object with the following methods:\n\n`getAsArray()`: Returns search result as array\n\n`getSortOrder()`: Returns sort order as string\n\n`getSearchCount()`: Returns search count (limit) as integer\n\n`getSearchOffset()`:  Returns search offset as integer\n\n`getResultCount()`: Returns the number of actual query results (\u003c= limit)\n\n`getTotalCount()`: Returns total result count (in the database)\n\n`getAllResults()`: Returns all results as array of `EntityDao` or `EntityModel` instances\n\n`getAllResultsAsArray()`: Returns all results as nested array (e.g. to serialize it as JSON)\n\n`getFirstResult()`: Returns first result `EntityDao` or `EntityModel` instance or throws an exception\n\n## Entity Configuration ##\n\nDAO entities are configured using protected class properties:\n\n```php\n\u003c?php\n\nprotected $_tableName = ''; // Database table name\nprotected $_primaryKey = 'id'; // Name or array of primary key(s)\nprotected $_fieldMap = array(); // 'db_column' =\u003e 'object_property'\nprotected $_hiddenFields = array(); // Fields that should be hidden for getValues(), e.g. 'password'\nprotected $_formatMap = array(); // 'db_column' =\u003e Format::TYPE\nprotected $_valueMap = array(); // 'object_property' =\u003e 'db_column'\nprotected $_timestampEnabled = false; // Automatically update timestamps?\nprotected $_timestampCreatedCol = 'created';\nprotected $_timestampUpdatedCol = 'updated';\n```\n\nPossible values for $_formatMap are defined as constants in `Doctrine\\ActiveRecord\\Dao\\Format`:\n\n```php\n\u003c?php\n\nconst NONE = '';\nconst INT = 'int';\nconst FLOAT = 'float';\nconst STRING = 'string';\nconst ALPHANUMERIC = 'alphanumeric';\nconst SERIALIZED = 'serialized';\nconst JSON = 'json';\nconst CSV = 'csv';\nconst BOOL = 'bool';\nconst TIME = 'H:i:s';\nconst TIMEU = 'H:i:s.u'; // Support for microseconds (up to six digits)\nconst TIMETZ = 'H:i:sO'; // Support for timezone (e.g. \"+0230\")\nconst TIMEUTZ = 'H:i:s.uO'; // Support for microseconds \u0026 timezone\nconst DATE = 'Y-m-d';\nconst DATETIME = 'Y-m-d H:i:s';\nconst DATETIMEU = 'Y-m-d H:i:s.u'; // Support for microseconds (up to six digits)\nconst DATETIMETZ = 'Y-m-d H:i:sO'; // Support for timezone (e.g. \"+0230\")\nconst DATETIMEUTZ = 'Y-m-d H:i:s.uO'; // Support for microseconds \u0026 timezone\nconst TIMESTAMP = 'U';\n```\n\nExample:\n\n```php    \n\u003c?php\n\nnamespace App\\Dao;\n\nuse Doctrine\\ActiveRecord\\Dao\\EntityDao;\n\nclass UserDao extends EntityDao\n{\n    protected $_tableName = 'users';\n    protected $_primaryKey = 'user_id';\n    protected $_timestampEnabled = true;\n}\n```\n\n## Business Models ##\n\n**Business Models** are logically located between **Controllers** - which render views and validate user input - and **Data Access Objects** (DAOs), that are low-level interfaces to a storage backend or Web service.\n\nPublic interfaces of models are high-level and should reflect all use cases within their domain. There are a number of standard use-cases that are pre-implemented in the base class `Doctrine\\ActiveRecord\\Model\\EntityModel`:\n\n`createModel(string $name = '', Dao $dao = null)`: Create a new model instance\n\n`find($id)`: Find a record by primary key\n\n`reload()`: Reload values from database\n\n`findAll(array $cond = array(), $wrapResult = true)`: Find multiple records; if `$wrapResult` is false, plain DAOs are returned instead of model instances\n\n`search(array $cond, array $options = array())`: Returns a `SearchResult` object ($options can contain count, offset, sort order etc, see search() in the DAO documentation above)\n\n`searchAll(array $cond = array(), $order = false)`: Simple version of search(), similar to findAll()\n\n`searchOne(array $cond = array())`: Search a single record; throws an exception if 0 or more than one record are found\n\n`searchIds(array $cond, array $options = array())`: Returns an array of matching primary keys for the given search condition\n\n`getModelName()`: Returns the model name without prefix and postfix\n\n`getId()`: Returns the ID of the currently loaded record (throws exception, if empty)\n\n`hasId()`: Returns true, if the model instance has an ID assigned (primary key)\n\n`getValues()`: Returns all model properties as associative array\n\n`getEntityTitle()`: Returns the common name of this entity\n\n`isDeletable()`: Returns true, if the model instance can be deleted with delete()\n\n`isUpdatable()`: Returns true, if the model instance can be updated with update($values)\n\n`isCreatable()`: Returns true, if new entities can be created in the database with create($values)\n\n`batchEdit(array $ids, array $properties)`: Update data for multiple records\n\n`getTableName()`: Returns the name of the associated main database table\n\n`hasTimestampEnabled()`: Returns true, if timestamps are enabled for the associated DAO\n\n`delete()`: Permanently delete the entity record from the database\n\n`save(array $values)`: Create a new record using the values provided\n\n`update(array $values)`: Update model instance database record; before assigning multiple values to a model instance, data should be validated using a form class\n\n**How much validation should be implemented within a model?** Wherever invalid data can lead to security issues or major inconsistencies, some core validation rules must be implemented in the model layer. Model exception messages usually don’t require translation (in multilingual applications), since invalid values should be recognized beforehand by a form class. If you expect certain exceptions, you should catch and handle them in your controllers.\n\nModels are associated with their respective Dao using a protected class property:\n\n```\nprotected $_daoName = ''; // DAO class name without namespace or postfix\n```\n\nExample:\n\n```php\n\u003c?php\n\nnamespace App\\Model;\n\nuse Doctrine\\ActiveRecord\\Model\\EntityModel;\n\nclass User extends EntityModel\n{\n    protected $_daoName = 'User';\n\n    public function delete() \n    {\n        $dao = $this-\u003egetEntityDao();\n        $dao-\u003eis_deleted = 1;\n        $dao-\u003eupdate();\n    }\n\n    public function undelete() \n    {\n        $dao = $this-\u003egetEntityDao();\n        $dao-\u003eis_deleted = 0;\n        $dao-\u003eupdate();\n    }\n\n    public function search(array $cond, array $options = array()) \n    {\n        $cond['is_deleted'] = 0;\n        return parent::search($cond, $options);\n    }\n\n    public function getValues()\n    {\n        $result = parent::getValues();\n        unset($result['password']);\n        return $result;\n    }\n}\n```\n\n## Unit Tests ##\n\nThis library comes with a `docker-compose.yml` file for MySQL and database fixtures to run unit tests (MySQL will bind to 127.0.0.1:3308):\n\n```\nlocalhost# docker-compose up -d\nlocalhost# docker-compose exec mysql sh\ndocker# cd /share/src/Tests/_fixtures\ndocker# mysql -u root --password=doctrine doctrine-active-record \u003c schema.sql\ndocker# exit\nlocalhost# bin/phpunit \nPHPUnit 7.3.2 by Sebastian Bergmann and contributors.\n\n................................................................. 65 / 91 ( 71%)\n..........................                                        91 / 91 (100%)\n\nTime: 251 ms, Memory: 8.00MB\n\nOK (91 tests, 249 assertions)\nlocalhost# docker-compose down\n\n```\n\n## Composer ##\n\nTo use this library in your project, simply run `composer require symlex/doctrine-active-record` or\nadd \"symlex/doctrine-active-record\" to your [composer.json](https://getcomposer.org/doc/04-schema.md) file and run `composer update`:\n\n```json\n{\n    \"require\": {\n        \"php\": \"\u003e=7.1\",\n        \"symlex/doctrine-active-record\": \"^4.0\"\n    }\n}\n```\n\n## About ##\n\nDoctrine ActiveRecord is maintained by [Michael Mayer](https://blog.liquidbytes.net/about/).\nFeel free to send an e-mail to [hello@symlex.org](mailto:hello@symlex.org) if you have any questions, \nneed [commercial support](https://blog.liquidbytes.net/contact/) or just want to say hello. \nWe welcome contributions of any kind. If you have a bug or an idea, read our \n[guide](CONTRIBUTING.md) before opening an issue.\n\n*Note: This library is part of [Symlex](https://symlex.org/) (a framework stack for agile Web development \nbased on Symfony) and not an official Doctrine project.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsymlex%2Fdoctrine-active-record","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsymlex%2Fdoctrine-active-record","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsymlex%2Fdoctrine-active-record/lists"}