Jam Package
Contents
1. Installation
- Require the package in Composer, by running
composer require bozboz/jam
- Add
Bozboz\Jam\Providers\JamServiceProvider::class
to the providers array config/app.php - NOTE: This must be above the RouteServiceProvider or routes will not sequence correctly. Add it to the pre-existing bozboz serviceproviders. - Run
php artisan vendor:publish && php artisan migrate
- Jump to 3. Usage for the default catchall route
2. Data Setup
2.1. Types
Entity types (a.k.a Types) are the top level of the jam schema. They can essentially be thought of as models, or a logical grouping of models (todo:clarify), since the templates actually have the fields. Types are registered in an app's service providers, eg
<?php
// todo: specify convention
[
'default_everything_entities' => // key used where?
new Type([
'menu_title' => 'Default Everything Entities',
'name' => 'Default Everything Entities', // used where?
]),
'custom_everything_entities' =>
new CustomType([
'menu_title' => 'Custom Everything Entities',
'name' => 'Custom Entities',
'report' => MyReport::class,
'link_builder' => MyLinkBuilder::class,
'menu_builder' => MyMenuBuilder::class,
'entity' => MyEntity::class,
'search_handler' => MySearchHandler::class,
'decorator' => MyDecorator::class
'can_restrict_access' => true, // used where?
'gated' => true, // used where?
'custom_attribute' => ''
])
];
Jam has a preregistered "pages" NestedType, since most apps are going to need one (NestedType is for entities requiring nested sort).
Type attributes
-
report
Admin report class for listing. Generally this won't ever need to be changed unless you're going for something completely custom. The NestedType automatically switches the default to NestedReport.Default
Bozboz\Admin\Reports\Report
-
link_builder
This is the class responsible for knowing what paths/URLs the type it's responsible for should generate. By default a type won't generate paths since it's not assumed that every entity is a standalone page. If you need your type to generate paths you may useBozboz\Jam\Entities\LinkBuilder
which will generate a single path based on the nesting of the current entity and update the paths of any descendant entities. For more complex entities that need to exist under more than 1 URL you can extend the LinkBuilder class and add your own path generation logic. The main path generated from the entity nest will be used as the canonical path for any additional paths.Default
Bozboz\Jam\Entities\LinksDisabled
-
menu_builder
Handles where to put the type in the admin menu. Types won't display in the menu until they have a template to base the entity on.-
Bozboz\Jam\Types\Menu\Hidden
don't display in menu -
Bozboz\Jam\Types\Menu\Content
display in content dropdown -
Bozboz\Jam\Types\Menu\Standalone
display as a top level menu item
Default
Bozboz\Jam\Types\Menu\Content
-
-
search_handler
Jam has some basic support for Elastic Search built in but is disabled by default. See 4. Search Indexing. -
entity
There are a few different types of entity classes in Jam that dictate sorting options. The normalBozboz\Jam\Entities\Entity
isn't sortable and will be sorted by name.Bozboz\Jam\Entities\SortableEntity
is manually sortable but alone will only allow sibling sorting. If nested sorting is required then it must be used in conjunction with theBozboz\Jam\Types\NestedType
type. Finally there's theBozboz\Jam\Entities\Post
entity that will sort the entities by date published.Default
Bozboz\Jam\Entities\Entity
2.2. Templates
Data fields for entity types are defined by adding/creating templates. Types can have multiple templates. To add a template, log in to the admin, click on "Jam" then pick a type to edit.
When adding a template you must give it a name
and view
but the listing view
and max_uses
values are optional. The view field will be used in the default render method to pick what view to actually render. The implementation of listing_view is largely down to the requirements but its intention is that you could have multiple templates in a type that require different views in a listing. Once you've created a template the admin menu will acknowledge the type and display it in the the menu wherever the configured menu_builder dictates. max_uses
allows you to limit the number of times a template may be used e.g. setting it to 1 will hide the option to create a new entity with that template after 1 is created (todo:example use case).
2.2.1. Migrating Templates
When you're making changes to templates in development the same changes will need to be made to the production database upon release. To aid with that there is a seeder generator and a history view for templates.
If you have created a brand new template that doesn't exist in the live database yet then your best bet it to use the seeder generator using the console command php artisan jam:make-seeder <type alias> <template alias>
. This will generate a seeder that can be run on the live database to insert the template, all its fields and field options. The seeders generated will have a name created from the type and template aliases suffixed with "JamTemplate.php".
eg. php artisan jam:make-seeder pages text-page
would generate a seeder named PagesTextPageJamTemplate.php
so once deployed to the live server it would be run using php artisan db:seed --class=PagesTextPageJamTemplate
If you're not creating whole new templates but rather changing parts of existing templates then the best thing to do is view the history for that template and manually redo the changes shown there on the production database after the work has been deployed.
2.3. Fields
A template is made up of a list of fields. Jam comes with the following field types:
-
Text
Standard singe line text input. -
Textarea
Standard multiline text input with a checkbox option to make it a WYSIWYG HTML editor. -
Image
Single media library field. -
Gallery
Multiselect media library field. -
Date
-
Date & Time
-
Toggle
Checkbox for toggling a boolean value. -
Entity List
Allows creation of multiple child entities. Useful repeated content structures like slider slides, call out boxes, etc.
In order to use this field type you must first set up another entity type that this field can link to using theBozboz\Jam\Types\EntityList
type.
e.g.<?php $mapper = $this->app['EntityMapper']; $mapper->register([ 'callout-boxes' => new \Bozboz\Jam\Types\EntityList('Callout Boxes'), ]);
-
Belongs To
Allows you to link one entity to another.en in the create/edit form and all entities created will have the selected parent. Selecting only a type or template will give the user the option of selecting the parent entity from entities from the selected type/templates.
You may also select whether or not entities with this field become nested under the related entity as child pages.
NOTE: This should not be used with sortable entities that don't display in the same listing as resorting them can potentially lead to unexpected tree manipulation.
-
Belongs To Many
Provides the entity form with an option to select from many entities to relate to. As withBelongs To
the options in the dropdown will be limited to the type and template selected when adding the field to the template. -
Inverse Belongs To Many (read-only)
The reverse of the relationship defined in aBelongs To Many
field, providing a read-only view to the related entities. -
Hidden
Allows you to add a hidden field to the create/edit form of entities that will save the value entered when the field is created.
If you needed any functionality not listed above (eg. to define a relationship between an entity template and an app's custom model not stored in Jam) you may create any number of custom field types by extending the Bozboz\Jam\Fields\Field
class and registering the field type in a service provider using the FieldMapper
registered in the service container.
2.4. Entities
Generally you won't need more than the default functionality in the package and you shouldn't interact directly with the Entity class itself. See 2.4. Entities for info on how to use different entity classes and 3.2. EntityRepository for info on how to fetch entities.
2.5. Revisions
Every time you save an entity it will create a revision in the entity_revisions table and a new set of values. This allows you to track changes across entities or revert back to previous states.
3. Usage
3.1. Catchall Route
Jam doesn't have any frontend routes set up by default but it does have a controller that your app can point some routes at.
Generally you'll want to add a catchall route right at the end of your routes file which will handle most if not all of your entity routing. This will use the paths table to lookup the entity based on the request path and serve it up in the view its template has configured.
<?php
/**
* Jam Catch All
*/
Route::get('{entityId}{slug?}', [
'as' => 'entity-by-id',
'uses' => '\Bozboz\Jam\Http\Controllers\EntityController@forId'
])->where('entityId', '(\d+)')->where('slug', '(/.+)?');
Route::get('{entityPath}', [
'as' => 'entity',
'uses' => '\Bozboz\Jam\Http\Controllers\EntityController@forPath',
])->where('entityPath', '(.+)?');
3.2. EntityRepository
Manual retrieval of entities should be done using the forType
method on the Bozboz\Jam\Repositories\EntityRepository
class. This will return a new query builder for the correct entity class registered to the type and limited to entities of that type.
3.3. Listings & Other Data
Some pages will require more data than just the entity so you will need to create a view composer for the current view. These should be added to app/Http/ViewComposers
and registered in the boot method of a ComposerServiceProvider service provider.
3.4. Canonical Paths
Every entity with a link builder will have a canonical path which most of the time will be the path you'll want to use when linking to it from other pages/menus. When querying entities you should use the withCanonicalPath
scope in order to eager load it or when working with a collection use the loadCanonicalPath
method to lazy load it. Use $entity->canonical_path
to output it.
3.5. Value Retrieval
Just querying the entities will only give you the data from the entities table, in order to load the values you must call the loadFields
method on either a single or collection of entities. The method takes a list of fields to load or will load all fields if no arguments given.
e.g.
<?php
$pages = $entityRepository->forType('page')->get()->loadFields('content', 'image');
If you need to load values for a belongs to or belongs to many entity relation then you can use the loadRelationFields(string $relationName, mixed $field/s)
method. This will also check that the relation itself is loaded and load it if not so you don't have to load it beforehand.
e.g.
<?php
$entities->loadRelationFields('categories', ['description', 'image']);
4. Search Indexing
Jam supports indexing entities via elastic search but each type needs to be set up with a search handler, see 2.1. Types. If all you need to be indexed is the name of the entity then you can just use the base Bozboz\Jam\Entities\Indexer
class. In the likely event that you need more than that each type will need a handler written for it. When setting up a search handler class for an entity type it should extend the bas Indexer class and override the getPreviewData
and getSearchableData
methods.
The purpose of getPreviewData
is to transform the entity in to a suitable format for your search results view and getSearchableData
is for returning all the values you want to be searchable as one long string.
e.g.
<?php
protected function getPreviewData($page)
{
return [
'preview' => str_limit(strip_tags(
$page->intro_text ?: $page->content
), 200),
'image' => Media::getFilenameOrFallback($page->image, null, 'search-result'),
];
}
protected function getSearchableData($page)
{
return strip_tags(implode(' ', [
$page->intro_text,
$page->content,
]));
}