XML Feeds

Objective: Create an efficient way to use a Symfony CollectionType of Entities. I wanted to be able to provide a Dijit FilteringSelect (autocomplete) for a collection of entities.

Issues: Using a CollectionType of EntityTypes loaded all the entities into a selection statement. This is fine if there are only a few entities, but not if there are hundreds. Also, model transformers cannot be used with CollectionTypes.

The custom field type is based off a TextType, which allows the id of the entity to be passed as text, then transformed by the transformer. Transformers are described here.

The Dijit FilteringSelect only needs an id to serve as an anchor within the page layout. One may argue it could all be dynamic, but I like to use the HTML as a foundation. A text input is used as the base element which is replaced by the Dijit widget. The widget is created and populated with the data from a store.

This is a modified version of the earlier post which created a static JSON representation of a menu.

What makes it different is that the earlier version rendered the items from the MenuBuilder into a static JSON object that was embedded in a page with a script tag. This approach removes the script tag (and the unnecessary twig template elements) and switches to a more graceful and efficient lazy loading menu.

The menu renderer is organizing the menu into the JSON format used by a Dijit Tree. This is set up for lazy loading, so it it only delivering the items in the assets tree. Items with URLs are pages, which you may click on to view the page, or expand to load more of the menu. These elements are defined in the menu builder (which isn't included in this post).

Dijit/Tree JSON

Code

{

"id": "admin-assets",

"name": "Assets",

"uri": null,

"has_children": true,

"parent": "admin",

"children": [

{

"id": "equipment",

"name": "Equipment",

"uri": "\/admin\/asset\/equipment",

"has_children": false,

"parent": "admin-assets"

},

{

"id": "issues",

"name": "Issues",

"uri": "\/admin\/issue\/issue",

"has_children": false,

"parent": "admin-assets"

},

{

"id": "manufacturers",

"name": "Manufacturers",

"uri": "\/admin\/asset\/manufacturer",

"has_children": true,

"parent": "admin-assets"

},

{

"id": "trailers",

"name": "Trailers",

"uri": "\/admin\/asset\/trailer",

"has_children": true,

"parent": "admin-assets"

},

{

"id": "vendors",

"name": "Vendors",

"uri": "\/admin\/asset\/vendor",

"has_children": true,

"parent": "admin-assets"

},

{

"id": "configuration",

"name": "Configuration",

"uri": null,

"has_children": true,

"parent": "admin-assets"

}

]

}

Content Menu Options

The content menu options allow the user to navigate directly to content they have created in the system. In this case, it is manufacturers. As above, if a URL is provided, the user may go to the page indicated by the uri attribute, or expand the menu item to view the children. The children under a manufacturer menu item are brands.

The URL is /api/menustore/adminmenus/manufacturers.

Code

{

"children": [

{

"id": "manufacturer-14",

"name": "Pelican",

"parent": "manufacturer",

"has_children": "1",

"uri": "\/admin\/asset\/manufacturer?name=Pelican"

},

{

"id": "manufacturer-2",

"name": "Shed",

"parent": "manufacturer",

"has_children": "0",

"uri": "\/admin\/asset\/manufacturer?name=Shed"

},

{

"id": "manufacturer-1",

"name": "Barn",

"parent": "manufacturer",

"has_children": "1",

"uri": "\/admin\/asset\/manufacturer?name=Barn"

}

]

}

Lazy loaded element

The content menu for the manufacturer named Barn follows. This leaf of the tree doesn't have any children. Clicking on it will take the user to the URL in the uri attribute.

The URL is /api/menustore/adminmenus/manufacturer-1

Code

{

"children": [

{

"id": 12,

"name": "Barn",

"parent": "manufacturer-1",

"uri": "\/admin\/asset\/manufacturer\/Barn\/brand\/Barn",

"has_children": false,

"children": null

}

]

}

This can be read as the manufacturer's name is Barn and they have one brand, Barn. If you click on the brand Barn under the manufacturer Barn, you will go to a page to manage the brand Barn.

The render code which returns the menu rendered ready for a Dijit/Tree. Reference.

app_admin_api_menu_store_get_adminmenu GET ANY ANY /api/menustore/adminmenus
app_admin_api_menu_store_get_adminmenu_alt GET ANY ANY /api/menustore/adminmenus/
app_admin_api_menu_store_get_adminmenu_parent GET ANY ANY /api/menustore/adminmenus/?parent={parent}
app_admin_api_menu_store_get_adminmenu_id GET ANY ANY /api/menustore/adminmenus/{id}

The code which delivers the admin menu follows.

This code is delivering the static menu elements, those defined in the MenuBuilder, using the JsonRenderer listed above.

They provide references which allow the menu to lazy load additional content.

This code handles the lazy loading. The id may be manufacturers or manufacturer-id. If it is the former, a list of the manufacturers will be returned, the later will return all the brands for a manufacturer. If the system is likely to have a large number of manufacturers or sub items, you should add another layer to organize the elements by the first letter of their name - so you can click on A to see all the manufacturers whose name starts with A.

PHP

function getManufacturerMenu( $adminMenu, $renderer, $id )

{

$limit = 2500; // TODO: Change to deliver first letters if there are too many manufacturers

It can be difficult for the client to know if the session has timed out on the server. This post describes the use of session storage to save and restore the data on a page input when a session times out.

Let's start with the timeout detection.

On the server side, this tiny piece of code checks if the user is logged in.