After months of hard work, our tiny team is finally going public with Elgg 3 beta. It’s an exciting release with interesting new features, improved dev usability and a cleaner code base.

In the following months, I will write more about specific features that I personaly worked on, the logic and reasoning behind them, as well as how to apply them in practice, but for now, I will give you a scoop of what to expect from Elgg 3.0.

Entity Lists and Query Builder

All of function have been consolidated. You can now query the database against all entity properties (metadata, annotations, private settings) using versatile . Thanks to a normalization layer doing the dirty work in the background, we have managed to keep this rewrite backwards compatible, so all of the legacy queries will work, for the most part, without changes.

Database operations are now powered by Elgg’s, which makes it easier to build complex entity queries using Doctrine’s DBAL. We have already rewritten a large number of raw SQL queries using the new fully-tested query API, and the intention is to eliminate Elgg’s dependence on MySQL — I am looking forward to the day, when we can run unit tests with SQLite.

Aside from testability and added security, Query Builder provides helpful utilities for joining Elgg tables and comparing unsanitized scalar and non-scalar values. Here is an example of what you can do with the new query builder:

'wheres' => function(\Elgg\Database\QueryBuilder $qb, $alias) {
$joined_alias = $qb->joinMetadataTable($alias, 'guid', 'status');
return $qb->compare("$", 'in', ['draft', 'unsaved_draft'], ELGG_VALUE_STRING);

There are also decorators for specific CRUD DB operations:

$qb = \Elgg\Database\Select::fromTable('entities', 'e');
->where($qb->compare('e.time_created', '>=', strtotime('-1 year')));

Schema Changes

A lot has been done to simplify the database schema.

We have thrown out secondary entity tables and moved secondary attributes into metadata. This has reduced complexity surrounding entity fetches, and made DB searches more uniform.

Metastrings table is finally gone. What seemed like a good idea at the early days of Elgg — normalizing property values into a metastrings table and referencing their IDs in metadata and annotation tables— has turned into an evil API bloat and database queries worthy of a Guiness record. With Elgg 3.0, metadata and annotation values are stored in respective tables and querying for entities by their properties is a piece of cake. Furthermore, metadata has been stripped of ownership and access controls, making it a basic key/value storage.


Thanks to all the schema changes and query changes listed above, we were able to trash the old search plugin and write up a more robust search API. allows plugins to perform literal and tokenized searches with partial or exact matches throughout predefined entity fields, including metadata, annotations and private settings. It also allows plugins to integrate custom indexing engines, such as Solr or Elasticsearch. As everything Elgg, there are many extension points, which allows plugins flexibility in how they handle search.


Page handlers no more. Rejoice!

We have integrated Symfony’s routing component, which allows plugins to define (and easily override) named routes and generate URLs agnostically. Plugins can now build on top of core and other plugins’ pathes without all the work that used to go into hijacking partial routes. Changing a certain segment will not require going through hundreds of views and manually updating the URLs.

We will be developing routing API further to make working with resource views, menus, breadcrumbs etc. easier. There are already some utility functions that simplify breadcrumb stacking, resolving URLs from entity properties and doing other wonderful magical things that used to siphon hours of development time.

Base Theme

The theme was rewritten with modern browsers in mind. The new theme is fully responsive thanks to the flex-driven approach we have taken. We have formalized some of the theming conventions to make collaboration between plugins easier.

Thanks to CSS Crush plugins can now share variables in stylesheets, which will hopefully create an ecosystem of plugins that don’t look like brothers from different mothers.

A different approach to page layouts makes them less of a pain to deal with. Plugins no longer have to decide, which layout to use, instead all layouts use the default grid, and adapt based on presence of various layout elements. Additionally, it is now much easier to alter and extend layout (filter) tabs, so plugins can easily group related pages into coherent UX.

A number of UI elements have been updated, including the entity menu, user hover card and entity listings. We have tried to reduce the boilerplate and create a solid foundation for plugins to built upon.

Plugin Testing

Testing was one of the biggest challenges. Those familiar with Elgg would know how difficult it was to get started with plugin testing. That is no longer a case.

First of all, we have added a PHPUnit integration test suite and migrated all legacy simpletest tests, so you can now run tests against an actual database. Thanks to all the refactoring we have been doing on a decade-old code, we were able to transition most of the core API towards testable OOP. Additionally, we have created some mock services, which allow testing most of entity-related operations without the DB connection.

All the work we have be doing on HTTP responses in 2.x series has paid off, and we are now able to test most aspects of resource and action handling.

With all that, we have started testing core plugins, and the testing techniques used in core are now available to plugins.

There is a long way to go to get to 100% coverage, but engine is 70% covered, and we expect the coverage to grow steadily as we work on stable 3.x releases.

CLI tool

We have added a CLI tool based on Symfony Console component. You can now install Elgg using convenient command. We will be adding more commands in the future, but you can already run cron without wget and curl using , and seed the database with demo data using . Plugins can register their own commands using plugin hooks.

You can checkout 3.0 Upgrade Notes as well as the changelog for more detailed information about the release.

Special thanks to Jeroen, Jerome and Steve for all the hard work on this release. Thank you for putting up with my frequent outbursts and open source fatigue.

Full-stack developer, passionate about front-end frameworks, design systems and UX.