{"id":19039598,"url":"https://github.com/windwalker-io/datamapper","last_synced_at":"2026-03-15T05:42:24.532Z","repository":{"id":14232387,"uuid":"16939443","full_name":"windwalker-io/datamapper","owner":"windwalker-io","description":"[READ ONLY] DataMapper pattern object to access database.","archived":false,"fork":false,"pushed_at":"2021-02-18T06:54:21.000Z","size":317,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-12-21T15:09:08.572Z","etag":null,"topics":["database","datamapper","orm","php"],"latest_commit_sha":null,"homepage":"https://github.com/ventoviro/windwalker ","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/windwalker-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-02-18T06:32:18.000Z","updated_at":"2024-12-16T13:47:59.000Z","dependencies_parsed_at":"2022-09-03T10:01:46.642Z","dependency_job_id":null,"html_url":"https://github.com/windwalker-io/datamapper","commit_stats":null,"previous_names":["ventoviro/windwalker-datamapper"],"tags_count":83,"template":false,"template_full_name":null,"purl":"pkg:github/windwalker-io/datamapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fdatamapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fdatamapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fdatamapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fdatamapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/windwalker-io","download_url":"https://codeload.github.com/windwalker-io/datamapper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fdatamapper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30030839,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T00:31:48.536Z","status":"online","status_checked_at":"2026-03-03T02:00:07.650Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["database","datamapper","orm","php"],"created_at":"2024-11-08T22:17:48.107Z","updated_at":"2026-03-03T03:02:02.569Z","avatar_url":"https://github.com/windwalker-io.png","language":"PHP","readme":"# Windwalker DataMapper\n\n## Installation via Composer\n\nAdd this to the require block in your `composer.json`.\n\n``` json\n{\n    \"require\": {\n        \"windwalker/datamapper\": \"~3.0\"\n    }\n}\n```\n\n## Getting Started\n\n### Prepare Windwalker Database object\n\n``` php\nuse Windwalker\\Database\\DatabaseFactory;\n\n// Make the database driver.\n$db = DatabaseFactory::getDbo(\n    'mysql',\n\tarray(\n\t\t'driver'   =\u003e 'mysql',\n\t\t'host'     =\u003e 'localhost',\n\t\t'user'     =\u003e 'root',\n\t\t'password' =\u003e 'xxxx',\n\t\t'database' =\u003e 'mydb',\n\t\t'prefix'   =\u003e 'prefix_'\n\t)\n);\n```\n\nThe DatabaseDriver will be cached in Factory, now DataMapper will auto load database driver.\n\nSee [Database](https://github.com/ventoviro/windwalker-database#windwalker-database)\n\n## Create DataMapper\n\n``` php\nuse Windwalker\\DataMapper\\DataMapper;\n\n$fooMapper = new DataMapper('#__foo');\n\n$fooSet = $fooMapper-\u003efind(array('id' =\u003e 1));\n```\n\nInject DB to DataMapper\n\n``` php\n// $db is Windwalker DatabaseDriver\n$mapper = new DataMapper('table', null, $db);\n```\n\nCustom primary keys:\n\n``` php\n// If keep keys NULL, the default `id` will auto set.\n$mapper = new DataMapper('table'); // Keys: array('id')\n\n// Set custom key\n$mapper = new DataMapper('table', 'table_id'); // Keys: array('table_id')\n\n// Set multiple keys\n$mapper = new DataMapper('table', array('table_id', 'uuid')); // Keys: array('table_id', 'uuid')\n```\n\n### Extend It\n\nYou can also create a class to operate specific table:\n\n``` php\nclass FooMapper extends DataMapper\n{\n    protected static $table = '#__foo';\n\n    protected static $keys = 'id';\n}\n\n$data = (new FooMapper)-\u003efindAll();\n```\n\nOr using facade:\n\n``` php\nuse Windwalker\\DataMapper\\AbstractDatabaseMapperProxy;\n\nabstract class FooMapper extends AbstractDatabaseMapperProxy\n{\n    protected $table = '#__foo';\n\n    protected $keys = 'id'; // Keep NULL will use default `id`\n}\n\n$data = FooMapper::findOne(array('id' =\u003e 5, 'alias' =\u003e 'bar'));\n```\n\n## Find Records\n\nFind method will fetch rows from table, and return `DataSet` class.\n\n### find()\n\nGet id = 1 record\n\n``` php\n$fooSet = $fooMapper-\u003efind(array('id' =\u003e 1));\n```\n\nFetch published = 1, and sort by `date`\n\n``` php\n$fooSet = $fooMapper-\u003efind(array('published' =\u003e 1), 'date');\n```\n\nFetch published = 1, language = en-US, sort by `date` DESC and start with `30`, limit `10`.\n\n``` php\n$fooSet = $fooMapper-\u003efind(array('published' =\u003e 1, 'language' =\u003e 'en-US'), 'date DESC', 30, 10);\n```\n\nUsing array, will be `IN` condition:\n\n``` php\n$fooSet = $fooMapper-\u003efind(array('id' =\u003e array(1,2,3))); // WHERE id IN (1,2,3)\n```\n\n### findOne()\n\nJust return one row.\n\n``` php\n$foo = $dooMapper-\u003efindOne(array('published' =\u003e 1), 'date');\n```\n\n### findAll()\n\nEqual to `find(array(), $order, $start, $limit)`.\n\n### Find With Custom Query\n\n``` php\n$fooMapper = new DataMapper('#__foo');\n\n$fooMapper-\u003ewhere('a = \"b\"') // Simple where\n\t-\u003ewhere('%n = $q', 'foo', 'bar') // Where format\n\t-\u003ewhere('flower = :sakura')-\u003ebind('sakura', 'Sakura') // Bind params\n\t-\u003eorWhere(array('c = d', 'e = f')) // AND (c=d OR e=f)\n\t-\u003ehaving('...')\n\t-\u003elimit(10, 20) // Limit, offset\n\t-\u003eorder('created DESC') // Can be array or string\n\t-\u003eselect(array('id', 'title', 'alias')) // Can be array or string\n\t-\u003efind();\n```\n\nThe available query methods.\n\n- `join($type = 'LEFT', $alias, $table, $condition = null, $prefix = null)`\n- `leftJoin($alias, $table, $condition = null, $prefix = null)`\n- `rightJoin($alias, $table, $condition = null, $prefix = null)`\n- `nnerJoin($alias, $table, $condition = null, $prefix = null)`\n- `outerJoin($alias, $table, $condition = null, $prefix = null)`\n- `call($columns)`\n- `group($columns)`\n- `order($columns)`\n- `limit($limit = null, $offset = null)`\n- `select($columns)`\n- `where($conditions, ...$args)`\n- `orWhere($conditions)`\n- `having($conditions, ...$args)`\n- `orHaving($conditions)`\n- `clear($clause = null)`\n- `bind($key = null, $value = null, $dataType = \\PDO::PARAM_STR, $length = 0, $driverOptions = array())`\n\nSee [Query Format](https://github.com/ventoviro/windwalker-query#format)\n\n## Create Records\n\nUsing DataSet to wrap every data, then send this object to create() method, these data will insert to table.\n\n### create()\n\n``` php\nuse Windwalker\\Data\\Data;\nuse Windwalker\\Data\\DataSet;\n\n$data1 = new Data;\n$data1-\u003etitle = 'Foo';\n$data1-\u003eauhor = 'Magneto';\n\n$data2 = new Data(\n    array(\n        'title' =\u003e 'Bar',\n        'author' =\u003e 'Wolverine'\n    )\n);\n\n$dataset = new DataSet(array($data1, $data2));\n\n$return = $fooMapper-\u003ecreate($dataset);\n```\n\nThe return value will be whole dataset and add inserted ids.\n\n```\nWindwalker\\Data\\DataSet Object\n(\n    [storage:ArrayObject:private] =\u003e Array\n        (\n            [0] =\u003e Windwalker\\Data\\Data Object\n                (\n                    [title] =\u003e Foo\n                    [auhor] =\u003e Magneto\n                    [id] =\u003e 39\n                )\n\n            [1] =\u003e Windwalker\\Data\\Data Object\n                (\n                    [title] =\u003e Bar\n                    [auhor] =\u003e Wolverine\n                    [id] =\u003e 40\n                )\n        )\n)\n```\n\n### createOne()\n\nOnly insert one row, do not need DataSet.\n\n``` php\n$data = new Data;\n$data-\u003etitle = 'Foo';\n$data-\u003eauhor = 'Magneto';\n\n$fooMapper-\u003ecreateOne($data);\n```\n\n\n## Update Records\n\nUpdate methods help us update rows in table.\n\n### update()\n\n``` php\nuse Windwalker\\Data\\Data;\nuse Windwalker\\Data\\DataSet;\n\n$data1 = new Data;\n$data1-\u003eid = 1;\n$data1-\u003etitle = 'Foo';\n\n$data2 = new Data(\n    array(\n        'id' =\u003e 2,\n        'title' =\u003e 'Bar'\n    )\n);\n\n$dataset = new DataSet(array($data1, $data2));\n\n$fooMapper-\u003eupdate($dataset);\n```\n\n### updateOne()\n\nJust update one row.\n\n``` php\n$data = new Data;\n$data-\u003eid = 1;\n$data-\u003etitle = 'Foo';\n\n$fooMapper-\u003eupdateOne($data);\n```\n\n### updateAll()\n\nUpdateAll is different from update method, we just send one data object, but using conditions as where\nto update every row match these conditions. We don't need primary key for updateAll().\n\n``` php\n$data = new Data;\n$data-\u003epublished = 0;\n\n$fooMapper-\u003eupdateAll($data, array('author' =\u003e 'Mystique'));\n```\n\n## Delete\n\nDelete rows by conditions.\n\n### delete()\n\n``` php\n$boolean = $fooMapper-\u003edelete(array('author' =\u003e 'Jean Grey'));\n```\n\n## Join Tables\n\nUse `newRelation()` to create a DataMapper and join other tables.\n\n``` php\nuse Windwalker\\DataMapper\\DataMapper;\n\n$items = DataMapper::newRelation('flower', '#__flower')\n\t-\u003eleftJoin('author', '#__users', 'flower.user_id = author.id')\n\t-\u003einnerJoin('category', '#__categories', array('category.lft \u003e= flower.lft', 'category.rgt \u003c= flower.rgt'))\n\t-\u003ewhere('flower.id = 1')\n\t-\u003eorder('created DESC')\n\t-\u003egroup('category.id')\n\t-\u003efind();\n```\n\nThe Join query will be:\n\n``` sql\nSELECT `flower`.`id`,\n\t`flower`.`catid`,\n\t`flower`.`title`,\n\t`flower`.`user_id`,\n\t`flower`.`meaning`,\n\t`flower`.`ordering`,\n\t`flower`.`state`,\n\t`flower`.`params`,\n\t`author`.`id` AS `author_id`,\n\t`author`.`name` AS `author_name`,\n\t`author`.`pass` AS `author_pass`,\n\t`category`.`id` AS `category_id`,\n\t`category`.`title` AS `category_title`,\n\t`category`.`ordering` AS `category_ordering`,\n\t`category`.`params` AS `category_params`\nFROM #__foo AS foo\n    LEFT JOIN #__users AS author ON foo.user_id = author.id\n    INNER JOIN #__categories AS category ON category.lft \u003e= foo.lft AND category.rgt \u003c= foo.rgt\nWHERE\n    flower.id = 1\nORDER BY flower.created DESC\nGROUP BY category.id\n```\n\nWhere condition will auto add alias if not provided.\n\n``` php\n$fooMapper-\u003efind(array(\n    'foo.id' =\u003e 3 // This is correct condition\n    'state' =\u003e 1 // This field may cause column conflict, DataMapper will auto covert it to `foo.state` =\u003e 1\n));\n```\n\nReset all tables and query:\n\n``` php\n$fooMapper-\u003ereset();\n```\n\n### Using OR Condition\n\n``` php\n$fooMapper-\u003eaddTable(\n    'category',\n    '#__categories',\n    'category.lft \u003e= foo.lft OR category.rgt \u003c= foo.rgt',\n    'LEFT'\n);\n```\n\n### Group\n\n``` php\n$fooMapper-\u003egroup('category.id');\n```\n\n## Compare objects\n\nUsing Compare objects help us set some where conditions which hard to use array to defind.\n\n``` php\n$fooSet = $fooMapper-\u003efind(\n    array(\n        new GteCompare('id', 5),\n        new NeqCompare('name', 'bar')\n        new LtCompare('published', 1),\n        new NinCompare('catid', array(1,2,3,4,5))\n    )\n);\n```\n\nThis will generate where conditions like below:\n\n``` sql\nWHERE `id` \u003e= '5'\n    AND `name` !== 'bar'\n    AND `published` \u003c '1'\n    AND `catid` NOT IN (1,2,3,4,5)\n```\n\n### Available compares:\n\n| Name       | Description      | Operator |\n| ---------- | -----------------| -------- |\n| EqCompare  | Equal                 | `=`  |\n| NeqCompare | Not Equal             | `!=` |\n| GtCompare  | Greater than          | `\u003e`  |\n| GteCompare | Greater than or Equal | `\u003e=` |\n| LtCompare  | Less than             | `\u003c`  |\n| LteCompare | Less than or Equal    | `\u003c=` |\n| InCompare  | In                    | `IN` |\n| NinCompare | Not In                | `IN` |\n\n### Custom Compare\n\n``` php\necho (string) new Compare('title', '%flower%', 'LIKE');\n```\n\nWill be\n\n``` sql\n`title` LIKE `%flower%`\n```\n\nSee: https://github.com/ventoviro/windwalker-compare\n\n## Using Data and DataSet\n\nSee: https://github.com/ventoviro/windwalker-data\n\n## Hooks\n\nAdd `\"windwalker/event\": \"~3.0\"` to `composer.json`.\n\nThen we are able to use hooks after every operations.\n\n``` php\nclass FooListener\n{\n    public function onAfterCreate(Event $event)\n    {\n        $result = $event['result'];\n\n        // Do something\n    }\n}\n\n$mapper = new DataMapper('table');\n\n// Add object as listener\n$mapper-\u003egetDispatcher()-\u003eaddListener(new FooListener);\n\n// Use listen() to add a callback as listener\n$mapper-\u003egetDispatcher()-\u003elisten('onAfterUpdate', function () { ... });\n\n$mapper-\u003ecreate($dataset);\n```\n\nExtends DataMapper:\n\n``` php\nclass SakuraMapper extends DataMapper\n{\n    protected $table = 'saluras';\n\n    public function onAfterFind(Event $event)\n    {\n        $result = $event['result'];\n\n        // Find some relations\n    }\n}\n\n$mapper = new DataMapper('table');\n$mapper-\u003efind(array('id' =\u003e 5));\n```\n\nAvailable events:\n\n- onBeforeFind\n- onAfterFind\n- onBeforeFindAll\n- onAfterFindAll\n- onBeforeFindOne\n- onAfterFindOne\n- onBeforeFindColumn\n- onAfterFindColumn\n- onBeforeCreate\n- onAfterCreate\n- onBeforeCreateOne\n- onAfterCreateOne\n- onBeforeUpdate\n- onAfterUpdate\n- onBeforeUpdateOne\n- onAfterUpdateOne\n- onBeforeUpdateBatch\n- onAfterUpdateBatch\n- onBeforeSave\n- onAfterSave\n- onBeforeSaveOne\n- onAfterSaveOne\n- onBeforeFlush\n- onAfterFlush\n- onBeforeDelete\n- onAfterDelete\n\nMore about Event: [Windwalker Event](https://github.com/ventoviro/windwalker-event)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwindwalker-io%2Fdatamapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwindwalker-io%2Fdatamapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwindwalker-io%2Fdatamapper/lists"}