{"id":1141,"date":"2013-11-01T22:03:51","date_gmt":"2013-11-01T21:03:51","guid":{"rendered":"https:\/\/www.entropywins.wtf\/blog\/?p=1141"},"modified":"2014-03-17T23:28:24","modified_gmt":"2014-03-17T22:28:24","slug":"new-database-abstraction-layer-for-mediawiki","status":"publish","type":"post","link":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/","title":{"rendered":"New database abstraction layer for MediaWiki"},"content":{"rendered":"<div id=\"pN7RmhDXItdrT10z52+dK7YPKS3O1B0IIL9sfNM3k3w=_14214927665:2276b2a:c773d138_entryContent\" itemprop=\"description\">\n<p><small>Disclaimer: the views and opinions expressed in this blog post are my own, and do not reflect those of Wikmedia Germany or my colleagues.<\/small><\/p>\n<p>At <a href=\"https:\/\/wikimedia.de\/\" target=\"_blank\">Wikimedia Germany<\/a>, we\u2019ve created a library that acts as database abstraction layer and which is largely build on top of the <a href=\"https:\/\/www.mediawiki.org\/\" target=\"_blank\">MediaWiki<\/a> database abstraction layer.<\/p>\n<p>This post explains the reasoning behind doing so, as well as some lessons learned along the way. Technical documentation, as well as installation and usage instructions, can be found in the documentation of this new library, linked at the end of this blog post.<\/p>\n<p>One of the components being developed for the <a href=\"https:\/\/www.wikidata.org\/\" target=\"_blank\">Wikidata<\/a> project has a table per fundamental type of value we store. Numbers, strings, entity ids, geo coordinates, etc, all have their own table. The user of this software can decide which of these fundamental values are supported. This makes the schema dependent on configuration. The MediaWiki mechanism for setting up and (structurally) updating tables does not support this well.<\/p>\n<p>The exact same situation has already been encountered by the <a href=\"https:\/\/semantic-mediawiki.org\/\" target=\"_blank\">Semantic MediaWiki<\/a> extension. The solution employed by SMW is dynamic schema generation. Unfortunately the quality of the code doing this is very low, so even if it were separable (which it is not), we\u2019d not want to use it.<\/p>\n<p>Hence we decided to write a new component providing schema modification functionality. In other words, it\u2019d be able to create tables, modify them, and delete them. And so <a href=\"https:\/\/github.com\/wmde\/WikibaseDatabase\" target=\"_blank\">Wikibase Database<\/a> was born.<\/p>\n<p>Creating such a new component allowed us to achieve another important goal as well: minimization of binding and dependencies. The component with dynamic schema depends on the <a href=\"https:\/\/www.mediawiki.org\/wiki\/Extension:Wikibase_DataModel\" target=\"_blank\">Wikibase DataModel<\/a> and its dependencies, and nothing else. It does not depend on Wikibase Repo, Wikibase Client, or MediaWiki.<\/p>\n<p>The separation for MediaWiki is done by creating a new interface (<a href=\"https:\/\/github.com\/wmde\/WikibaseDatabase\/blob\/master\/src\/QueryInterface\/QueryInterface.php\" target=\"_blank\">QueryInterface<\/a>) which is used by the consumers of Wikibase Database. In Wikibase Database it is implemented by a MediaWiki specific class that is essentially a thin adaptor around MediaWiki\u2019s DatabaseBase.<\/p>\n<p>On top of breaking dependence on the whole of MediaWiki, this new interface also takes care of some isolating consumers of Wikibase Database from several flaws in DatabaseBase.<\/p>\n<p>First of all, DatabaseBase is bloated. It is extremely poorly <a href=\"https:\/\/en.wikipedia.org\/wiki\/Interface_segregation_principle\" target=\"_blank\">segregated<\/a>. The interfaces in Wikibase Database were carefully designed to avoid such a mistake. Secondly, the methods in DatabaseBase tend to inappropriately return error codes on failures, rather then exceptions. Wikibase Database catches these and throws nice fine grained exceptions with appropriate information. Lastly the MediaWiki interface has inconsistent implementations. The MySQL and SQLite implementations behave differently in certain error conditions, which really ought to not be exposed to the users of this interface.<\/p>\n<p>One lesson learned is that really everything that can be separated from database interaction should be, if you want it to be testable. Initially I created a \u201cMySQLTableBuilder\u201d that has a createTable( TableDefinition $table ) method. Seems harmless enough right? Let\u2019s describe the responsibility of this class: build the table creation SQL based on the provided table definition <em>and<\/em> execute it. Note how this description contains an \u201cand\u201d, which is a good sign of a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Single_responsibility_principle\" target=\"_blank\">Single Responsibility Principle <\/a>violation.<\/p>\n<p>The result of this poorly thought out design was that testing the meat of this class, the building of the table creation SQL, was not easily possible without database interaction. (Mocking was quite difficult in this scenario due to the nature of the MW DB abstraction layer).<\/p>\n<p>A better design, which seems quite obvious in retrospect, is to put the table creation SQL building code into its own class, ie MySQLTableSqlBuilder. This class implements a TableSqlBuilder interface, which is used as strategy at appropriate locations, and has an implementation for each supported database type.<\/p>\n<p>Another lesson learned is that it really pays to have really distinct boundaries within the package itself. MySQL specific and\u00a0MediaWiki specific code are both in their own namespace. Schema modification code and \u201cregular db interaction\u201d code are also kept apart. The documentation clearly describes the responsibility of each part of the component, which other parts it is dependent on, and if it contains interfaces that can be used from outside the component or not. This makes understanding the component a lot easier, increases separability, and makes it easier to use it correctly.<\/p>\n<p>We just released version 0.1 of this component, as it reached sufficient stability for third party usage. I personally hope that it will be of use to people building on top of MediaWiki, or as indeed is possible, to those who are doing PHP development in a different context.<\/p>\n<p>For download and usage instructions, technical documentation, and release notes, see the <a href=\"https:\/\/github.com\/wmde\/WikibaseDatabase\/blob\/master\/README.md\" target=\"_blank\">README file on GitHub<\/a>.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Disclaimer: the views and opinions expressed in this blog post are my own, and do not reflect those of Wikmedia&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-1141","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>New database abstraction layer for MediaWiki - Blog of Jeroen De Dauw<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"New database abstraction layer for MediaWiki - Blog of Jeroen De Dauw\" \/>\n<meta property=\"og:description\" content=\"Disclaimer: the views and opinions expressed in this blog post are my own, and do not reflect those of Wikmedia&hellip;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/\" \/>\n<meta property=\"og:site_name\" content=\"Blog of Jeroen De Dauw\" \/>\n<meta property=\"article:published_time\" content=\"2013-11-01T21:03:51+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2014-03-17T22:28:24+00:00\" \/>\n<meta name=\"author\" content=\"Jeroen\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/JeroenDeDauw\" \/>\n<meta name=\"twitter:site\" content=\"@JeroenDeDauw\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeroen\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/\"},\"author\":{\"name\":\"Jeroen\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"headline\":\"New database abstraction layer for MediaWiki\",\"datePublished\":\"2013-11-01T21:03:51+00:00\",\"dateModified\":\"2014-03-17T22:28:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/\"},\"wordCount\":782,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/\",\"url\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/\",\"name\":\"New database abstraction layer for MediaWiki - Blog of Jeroen De Dauw\",\"isPartOf\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#website\"},\"datePublished\":\"2013-11-01T21:03:51+00:00\",\"dateModified\":\"2014-03-17T22:28:24+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.entropywins.wtf\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"New database abstraction layer for MediaWiki\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#website\",\"url\":\"https:\/\/www.entropywins.wtf\/blog\/\",\"name\":\"Entropy Wins\",\"description\":\"A blog on Software Architecture, Design and Craftsmanship\",\"publisher\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.entropywins.wtf\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\",\"name\":\"Jeroen\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g\",\"caption\":\"Jeroen\"},\"logo\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/jeroendedauw\/\",\"https:\/\/x.com\/https:\/\/twitter.com\/JeroenDeDauw\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"New database abstraction layer for MediaWiki - Blog of Jeroen De Dauw","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/","og_locale":"en_US","og_type":"article","og_title":"New database abstraction layer for MediaWiki - Blog of Jeroen De Dauw","og_description":"Disclaimer: the views and opinions expressed in this blog post are my own, and do not reflect those of Wikmedia&hellip;","og_url":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/","og_site_name":"Blog of Jeroen De Dauw","article_published_time":"2013-11-01T21:03:51+00:00","article_modified_time":"2014-03-17T22:28:24+00:00","author":"Jeroen","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/JeroenDeDauw","twitter_site":"@JeroenDeDauw","twitter_misc":{"Written by":"Jeroen","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/#article","isPartOf":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/"},"author":{"name":"Jeroen","@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"headline":"New database abstraction layer for MediaWiki","datePublished":"2013-11-01T21:03:51+00:00","dateModified":"2014-03-17T22:28:24+00:00","mainEntityOfPage":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/"},"wordCount":782,"commentCount":1,"publisher":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/","url":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/","name":"New database abstraction layer for MediaWiki - Blog of Jeroen De Dauw","isPartOf":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#website"},"datePublished":"2013-11-01T21:03:51+00:00","dateModified":"2014-03-17T22:28:24+00:00","breadcrumb":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/01\/new-database-abstraction-layer-for-mediawiki\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.entropywins.wtf\/blog\/"},{"@type":"ListItem","position":2,"name":"New database abstraction layer for MediaWiki"}]},{"@type":"WebSite","@id":"https:\/\/www.entropywins.wtf\/blog\/#website","url":"https:\/\/www.entropywins.wtf\/blog\/","name":"Entropy Wins","description":"A blog on Software Architecture, Design and Craftsmanship","publisher":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.entropywins.wtf\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7","name":"Jeroen","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g","caption":"Jeroen"},"logo":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/in\/jeroendedauw\/","https:\/\/x.com\/https:\/\/twitter.com\/JeroenDeDauw"]}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p74TBF-ip","jetpack-related-posts":[{"id":1340,"url":"https:\/\/www.entropywins.wtf\/blog\/2014\/05\/06\/wikibase-and-doctrine-dbal\/","url_meta":{"origin":1141,"position":0},"title":"Wikibase and Doctrine DBAL","author":"Jeroen","date":"2014-05-06","format":false,"excerpt":"When I started writing this blog post, I realized some introduction to the query components was first due. You can find it in my last blog post: The Wikidata phase3 software components. In this post I described how the SQLStore uses a database abstraction layer to not bind itself to\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2014\/05\/dbal.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2014\/05\/dbal.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2014\/05\/dbal.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2014\/05\/dbal.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2014\/05\/dbal.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":1337,"url":"https:\/\/www.entropywins.wtf\/blog\/2014\/05\/06\/the-wikidata-phase3-software-components\/","url_meta":{"origin":1141,"position":1},"title":"The Wikidata phase3 software components","author":"Jeroen","date":"2014-05-06","format":false,"excerpt":"Work on the long awaited query functionality for the Wikidata project has already happened during a period of several months. Since queries are a completely disjoint feature set from the existing functionality, we decided to put it into a new component part of the Wikibase software. This component is called\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":896,"url":"https:\/\/www.entropywins.wtf\/blog\/2010\/07\/15\/mediawiki-deployment\/","url_meta":{"origin":1141,"position":2},"title":"MediaWiki deployment","author":"Jeroen","date":"2010-07-15","format":false,"excerpt":"A lot has happened in my Google Summer of Code project since my last blog post about it, so here is another update. I did give a short presentation about it at Wikimania 2010, but that did not go very well unfortunately. You can get the slides though. I changed\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"MediaWiki deployment","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2010\/07\/800px-Mwdeployment.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2010\/07\/800px-Mwdeployment.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2010\/07\/800px-Mwdeployment.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2010\/07\/800px-Mwdeployment.jpg?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1383,"url":"https:\/\/www.entropywins.wtf\/blog\/2014\/08\/02\/optional-table-alias-in-the-doctrine-querybuilder\/","url_meta":{"origin":1141,"position":3},"title":"Doctrine QueryBuilder table alias","author":"Jeroen","date":"2014-08-02","format":false,"excerpt":"The Doctrine project, best known for its Object Relational Mapper, also includes a database abstraction layer, used by the ORM. This abstraction layer is called DBAL, for DataBase Abstraction Layer. Quickly after I started using DBAL in some Wikibase components, I got annoyed at how single table single table selects\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"dbal-old","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2014\/08\/dbal-old.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":962,"url":"https:\/\/www.entropywins.wtf\/blog\/2010\/08\/20\/end-of-google-summer-of-code-2010\/","url_meta":{"origin":1141,"position":4},"title":"End of Google Summer of Code 2010","author":"Jeroen","date":"2010-08-20","format":false,"excerpt":"As Google Summer of Code (GSoC) 2010 has ended, I'm writing this blog post to outline what I've done during the coding period and what the results are. Thanks go to the Wikimedia Foundation and Google for providing the opportunity to do this project, Brion Vibber, who mentored me, and\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"Google Summer of Code 2010","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2010\/08\/2010_NoURL_300x267px.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":1782,"url":"https:\/\/www.entropywins.wtf\/blog\/2016\/06\/24\/maps-3-7-for-mediawiki-released\/","url_meta":{"origin":1141,"position":5},"title":"Maps 3.7 for MediaWiki released","author":"Jeroen","date":"2016-06-24","format":false,"excerpt":"I\u2019m happy to announce the immediate availability of Maps 3.7. This feature release brings some minor enhancements. Added rotate control support for Google Maps (by Peter Grassberger) Changed coordinate display on OpenLayers maps from long-lat to lat-long (by Peter Grassberger) Upgraded Google marker cluster library to its latest version (2.1.2)\u2026","rel":"","context":"In &quot;Software&quot;","block_context":{"text":"Software","link":"https:\/\/www.entropywins.wtf\/blog\/category\/software\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/06\/suchrotate.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/06\/suchrotate.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/06\/suchrotate.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/06\/suchrotate.jpg?resize=700%2C400&ssl=1 2x"},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/1141","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/comments?post=1141"}],"version-history":[{"count":2,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/1141\/revisions"}],"predecessor-version":[{"id":1267,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/1141\/revisions\/1267"}],"wp:attachment":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/media?parent=1141"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/categories?post=1141"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/tags?post=1141"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}