Index
- How to install / upgrade.
- Backward compatibility breaks.
2.1. New installation folder.
2.2. New entities folder. - New Features.
3.1. New MVC.
3.2. HasMailer trait.
3.3. CreateRootCategory command.
3.4. CreateRootAsset command.
3.5. Default Access for Articles.
3.6. New helper to deal with namespaces.
3.7. Entity Defaults.
3.8. CreateUncategorisedCategory command.
3.9. HasSingleton trait.
3.10. Predefined user groups classes.
3.11. Predefined view levels classes.
3.12. New ArrayHelper.
3.13. New Request class.
3.14. New RouteGenerator class.
3.15. New HasLinks class.
3.16. New JSONResponse class.
3.17. DatabaseSearcher improvements.
1. How to install / upgrade
Download joomla-entity-v2.0.0.zip an install through Joomla! Extension Manager.
2. Backward compatibility breaks
2.1. New installation folder.
Now the library uses the namespaced library system. This means that the library is installed in:
/libraries/phproberto/joomla_entity
instead of :
/libraries/joomla_entity
This ensures that no collisions will happen with other solutions and also solves the bug that prevented the library from being loaded through JLoader
. Before you needed to include the library like:
require_once JPATH_LIBRARIES . '/joomla_entity/library.php';
Now the library should be included like:
JLoader::import('phproberto.joomla_entity.library');
2.2. New entities folder.
This is not strictly a B/C change because I kept old entities in their original places but you should start loading entities from their new folder.
Before entities were directly stored in the root "extension" folder:
src/Content
and now they are stored in their own specific folder which makes them easier to find manage and keeps structure cleaner:
src/Content/Entity
Not all the entities are now in this folder and this is why this change is introduced in a B/C way. All the entities will get moved to their Entity folders and v3.x will remove support for old entity locations. All the new entities will be generated also in the Entity folders.
3. New Features
3.1. New MVC.
Te ease the integration with components now Joomla Entity provides a full set of methods to create an API for your components easily.
Do you need to a controller with a basic CRUD? You only have to integrate these traits:
- HasEntityGet - Get a single entity data with
ajaxEntityGet()
method. Example:task=article.ajaxEntityGet
. You also havejsonEntityGet()
which is the same but doesn't validate that the request is sent through AJAX. - HasEntityCreate - Create a single entity
ajaxEntityCreate()
method. Example call:task=article.ajaxEntityCreate
. - HasEntityUpdate - Edit a single entity
ajaxEntityUpdate()
method. Example call:task=article.ajaxEntityUpdate
. - HasEntityDelete - Edit a single entity
ajaxEntityDelete()
method. Example call:task=article.ajaxEntityDelete
.
All those traits require that tell the controller which entity is associated. The easiest way is done also through a trait HasAssociatedEntity
.
So a full CRUD for articles can be done like:
use Phproberto\Joomla\Entity\Contracts\AssociatedEntity;
use Phproberto\Joomla\Entity\MVC\Controller\Traits\HasAssociatedEntity;
use Phproberto\Joomla\Entity\MVC\Controller\Traits\HasEntityCreate;
use Phproberto\Joomla\Entity\MVC\Controller\Traits\HasEntityDelete;
use Phproberto\Joomla\Entity\MVC\Controller\Traits\HasEntityGet;
use Phproberto\Joomla\Entity\MVC\Controller\Traits\HasEntityUpdate;
class Phproberto_ContentControllerArticle extends FormController implements AssociatedEntity
{
use HasAssociatedEntity, HasEntityCreate, HasEntityDelete, HasEntityGet, HasEntityUpdate;
/**
* Retrieve the associated entity class.
*
* @return string
*/
public function entityClass()
{
return Article::class;
}
}
And you already have an API for your extension.
3.2. HasMailer trait.
There are some cases where we need to send mails within classes. Now Joomla Entity provides the HasMailer
trait that can be used like:
use Phproberto\Joomla\Entity\Traits\HasMailer;
class PlgPhproberto_NotificationSomeEvent extends BasePlugin
{
use HasMailer;
protected function onSomeEvent()
{
$recipients = [
'[email protected]',
'[email protected]'
];
$subject = 'Hello world!';
$body = 'This is a <strong>sample message</strong>';
$this->sendMail($recipients, $subject, $body);
}
}
3.3. CreateRootCategory command.
In some cases like new installations or tests environments we need the root category to be created. Now Joomla entity includes a command for it:
use Phproberto\Joomla\Entity\Categories\Command\CreateRootCategory;
$rootCategory = CreateRootCategory::instance()->execute();
It also provides a way to transparently create the root category if not present:
use Phproberto\Joomla\Entity\Categories\Category;
// This will load the root category if present or create it if it does not exist
$rootCategory = Category::root();
3.4. CreateRootAsset command.
Exactly like the root category Joomla Entity now provides a method to create the root asset.
use Phproberto\Joomla\Entity\Core\Command\CreateRootAsset;
$rootAsset = CreateRootAsset::instance()->execute();
and a way to create it transparently if it does not exist:
use Phproberto\Joomla\Entity\Core\Entity\Asset;
// This will load the root asset if present or create it if it does not exist
$rootAsset = Asset::root();
3.5. Default Access for Articles.
Before we needed to explicitly declare the access associated to any new article like:
$article = Article::create(
[
'title' => 'My article',
'access' => PublicViewLevel::instanceOrCreate()->id()
]
);
Now you don't need to specify the access
view level if it's the Public
view level:
$article = Article::create(
[
'title' => 'My article'
]
);
3.6. New helper to deal with namespaces.
use Phproberto\Joomla\Entity\Helper\ClassName;
// Check if a class is in a namespace
if (ClassName::inNamespace($class))
{
// Do something
}
// Retrieve the class without the namespace. Returns Article for Phproberto\Joomla\Entity\Content\Entity\Article
$name = ClassName::withoutNamespace($class);
// Retrieve the parts of the class namespace
// Will return an array like: ['Phproberto', 'Joomla', 'Entity', 'Content', 'Entity']
$parts = ClassName::namespaceParts($class);
// Return the parent namespace of a class
// Will return something like for Phproberto\Joomla\Entity\Content for a class like Phproberto\Joomla\Entity\Content\Entity\Article
$parentNamespace = ClassName::parentNamespace($class);
3.7. Entity Defaults.
Now you can provide default values with your entities so only strictly required data is sent when trying to create an entity. Things like default access group, etc. will never be required again.
Defaults are only applied to an entity when it's saved for the first time (when creating it).
Let's use as example default applied to categoriesd (src/Categories/Category.php
):
/**
* Default data for new instances.
*
* @return []
*
* @since __DEPLOY_VERSION__
*/
public function defaults()
{
$parent = $this->hasParent() ? $this->parent() : self::root();
return [
'extension' => static::$extension,
CoreColumn::ACCESS => (int) $parent->get(CoreColumn::ACCESS),
CoreColumn::PARENT => $parent->id(),
CoreColumn::STATE => (int) $parent->get(CoreColumn::STATE)
];
}
As you can see new categories inherit access from their parent category and they are (if not specified) child categories of the root category.
3.8. CreateUncategorisedCategory command.
com_content usually provides a base
Uncategorised` category that is used to assign it to articles with no specific category assigned. This command creates it for us.
use Phproberto\Joomla\Entity\Content\Command\CreateUncategorisedCategory;
$uncategorisedCategory = CreateUncategorisedCategory::instance()->execute();
// Transparently create the uncategorised category if it does not exist:
use Phproberto\Joomla\Entity\Content\Entity\Category;
$uncategorisedCategory = Category::uncategorised();
3.9. HasSingleton trait.
This can be used for classes where a single instance should be used.
3.10. Predefined user groups classes.
There is now a list of predefined user groups to ease dealing with core stuff:
// Retrieves the Public user group or creates it
$public = PublicUserGroup::instanceOrCreate();
// You can also explicitly create a group (good for tests, etc.)
$guest = GuestUserGroup::create();
// All the predefined user groups:
$public = PublicUserGroup::instanceOrCreate();
$guest = GuestUserGroup::instanceOrCreate();
$authorGroup = AuthorUserGroup::instanceOrCreate();
$registeredGroup = RegisteredUserGroup::instanceOrCreate();
$managersGroup = ManagerUserGroup::instanceOrCreate();
$superUsersGroup = SuperUsersUserGroup::instanceOrCreate();
3.11. Predefined view levels classes.
As with groups there is now a list of predefined view levels to ease dealing with core stuff:
// Retrieves the Public view level or creates it
$public = PublicViewLevel::instanceOrCreate();
// You can also explicitly create a group (good for tests, etc.)
$guest = GuestViewLevel::create();
// All the predefined user view levels:
$public = PublicViewLevel::instanceOrCreate();
$guest = GuestViewLevel::instanceOrCreate();
$registered = RegisteredViewLevel::instanceOrCreate();
$special = SpecialViewLevel::instanceOrCreate();
$superUsers = SuperUsersViewLevel::instanceOrCreate();
3.12. New ArrayHelper.
There is usually a common sanitisation to be done when dealing with arrays:
$ids = ['', null, 1, '1', 0, 001, 2];
// Will return something like:
// [1, 2]
$sanitisedIds = ArrayHelper::toPositiveIntegers($ids);
3.13. New Request class.
This is a new class to deal with common request validations. It's also useful to fake requests objects on tests:
use Phproberto\Joomla\Entity\MVC\Request;
// You can specify a Joomla Input object
$request = new Request(new Input(['test' => 'value']));
// Check the active request is done through AJAX and contains a token sent in $_POST
if (!Request::active()->validateAjaxWithTokenOrCloseApp('post'))
{
}
// Check the active request contains a token sent in $_GET
if (!Request::active()->validateHasToken('get'))
{
}
3.14. New RouteGenerator class.
This class is usefull to generate routes for your components.
$vars = [
'task' => 'extension.publish',
'id' => 23
];
$options = [
'addToken' => true,
'return' => 'current',
'routed' => false,
'xhtml' => false
];
$generator = RouteGenerator::getInstance('com_phproberto');
// This will generate an url with a return and a token
// index.php?option=com_phproberto&task=extension.publish&id=23&return=ssaasdasdas213123&324342342312asdasd12=1
$url = $generator->generateUrl($vars, $options);
3.15. New HasLinks class.
A class to easily create common links for your entities. Sample usage:
use Phproberto\Joomla\Entity\Core\Traits\HasLinks;
class Car extends Entity
{
use HasLinks
}
// Usage
$entity = Car::find(1);
// Edit link: index.php?option=com_phproberto&car.edit&id=1&23423423345adf=1
$editLink = $entity->linkEdit();
// You can add custom variables to links:
// index.php?option=com_phproberto&car.add&id=1&color=pink&23423423345adf=1
$editLink = $entity->linkEdit(['color' => 'pink']);
// View link: index.php?option=com_phproberto&view=car&id=1
$link = $entity->linkView();
// Delete link: index.php?option=com_phproberto&car.delete&id=1&23423423345adf=1
$link = $entity->linkDelete();
// Edit link: index.php?option=com_phproberto&car.add&23423423345adf=1
$link = $entity->linkCreate();
// You can use it to generate any link: index.php?option=com_phproberto&view=sample&id=12
$link = $entity->link(['view'=> 'sample', 'id' => 12]);
3.16. New JSONResponse.
Because the way Joomla deals with app flow we cannot reuse Symfony's JsonRsponse
class. Joomla response always need to close the app. This new class makes response easy to return.
Examples:
use Phproberto\Joomla\Entity\MVC\JSONResponse;
$response = new JSONResponse;
// Return a 404 error
return $response->setStatusCode(404)
->setErrorMessage('Article not found')
->send();
// Return a 403 error
return $response->setStatusCode(403)
->setErrorMessage('Forbidden')
->send();
// Return a valid JSON response
return $response->setData(
[
'item' => $entity->all(),
'something' => 'some value'
]
)->send();
3.17. DatabaseSearcher improvements.
Now classes extending DatabaseSearcher
can provide pagination objects with their results. This is usually usefull to act as old list models. Or for fast generation of list controllers:
use Phproberto\Joomla\Entity\Content\Search\ArticleSearch;
use Phproberto\Joomla\Entity\MVC\JSONResponse;
$response = new JSONResponse;
$searched = new ArticleSearch(
[
'filter.access' => 1,
'list.limit' => 10
]
);
$data = [
'articles' => $searcher->search(),
'pagination' => $searcher->pagination()
];
return $response->setData($data)->send();
Additionally all the queries done by searchers are now statically cached. This means that you can repeat the call the same query twice in the same request and it will only be executed the first time.