Blog

Mura ORM Boot Camp: Instantiation & Relationships

April 14, 2017 by Grant Shepert

In this post I will discuss two topics related to Mura ORM, instantiation and relationships.

Object "instantiation" is the process of getting your Mura ORM object running and accessible within Mura CMS, and the term "relationships" in this context refers to the connection of Mura ORM objects not only to each other, but to the Mura CMS object model as well.

This second blog post in the series follows part 1Mura ORM has advanced greatly since publishing the initial one, and there is even more to play with! So, let's get to it.


Object Instantiation

How do I get my Mura ORM objects running? That is a fair, and important, question. There are several ways to do this.

Objects can be registered manually:

var ioc= $.getServiceFactory();
ioc.declareBean(beanName='myBeanName', dottedPath='default.includes.object.myobject', isSingleton = true/false );

By 'declaring' the bean, giving it a name and entering the dotted-notation path to the object, Mura will load or instantiate this Mura ORM bean during startup. Note that you will have to trigger this manually the first time by reloading Mura (putting ?appreload at the end of a page url is an easy way to do this).

This is handy when your objects are within a plugin or custom location. However, the much easier way is to have Mura look for and instantiate your custom Mura ORM beans automatically by using a simple "by convention" technique. This is done by creating a directory called "model" within your sites /includes directory.  Mura will also auto-wire the 'model' directory in several other locations:

  • within a custom display object (Mura 7 or later)
  • within a theme directory
  • within a display object (Mura 7 or later)
  • within custom content type directories aka ../content_types/{type}_{subtype}/model (Mura 7 or later)

You then create a directory within this 'model' directory called 'beans', and any Mura ORM object(s) you place in there will automatically be registered when Mura loads (again, you will have to manually trigger this the first time with ?appreload).

For example, lets say you have two Mura ORM objects I want to register with Mura CMS, myParentBean and myChildBean.

component extends="mura.bean.beanORM" entityname="myparentbean" table="my_myparentbean" { 
  property name="myparentbeanid" fieldtype="id";
} 
component extends="mura.bean.beanORM" entityname="mychildbean" table="my_mychildbean" { 
  property name="mychildbeanid" fieldtype="id";
} 

Place these within the /[siteid]/includes/model/beans/ directory. The next time Mura restarts, these two Mura ORM objects will be available via $.getBean('myParentBean') or $.getBean('myChildBean'). Simple!

You can, of course, also register your own custom directories if you chose. You can register a custom directory like this:

m.globalConfig().registerModelDir(dir='/path/to/model/') 

... and custom content type directories like this:

m.siteConfig().registerContentTypDir(dir='…')

... and display object directories like this:

m.siteConfig().registerDisplayObjectDir(dir='…')

Remember that this is only if you want to register a directory that is named something other than 'model'. If you put a directory named 'model' in any of these locations, any Mura ORM objects within it will be auto-wired automatically (i.e. by convention).

For plugins, you should place your registration code inside an init() or onApplicationLoad() event, like this:

function init(){
 variables.pluginConfig.registerModelDir(dir='path/to/model/from/plugin/root');
}

Note that plugins are the one location where auto-registration of the 'model' directory does not occur, due to backwards compatibility. Use the above code if you want to register an auto-wiring directory for your plugin.

 

Relationships

Relationships don't need to be complicated ... at least not where Mura ORM objects are concerned. Developers often find themselves needing to logically link objects to one another. For instance, in our above example, the myParentBean entity might have several myChildBean entities related to it. Think of a car with multiple doors or a house with many windows. These relationships can be broken down into three basic types:

  • one-to-one, where one entity is related precisely to one other entity (husband - wife)
  • one-to-many/many-to-one, where an entity is related to many other entities (parent - children)
  • many-to-many, where there can be any number of entities cross-related to another set of entities ( books - authors )

You can identify these relationships easily inside of a Mura ORM bean. Lets modify our above examples to include these relationships.

component extends="mura.bean.beanORM" entityname="myparentbean" table="my_myparentbean" {
 property name="myparentbeanid" fieldtype="id";
 property name="mychildren" fieldtype="one-to-many" relatesto="mychildbean";
}
component extends="mura.bean.beanORM" entityname="mychildbean" table="my_mychildbean" {
 property name="mychildbeanid" fieldtype="id";
 property name="myparent" fieldtype="many-to-one" relatesto="myparentbean";
}

Note that I have explicitly identified this relationship in both objects. This isn't specifically required, as the reciprocal one-to-many/many-to-one relationship is assumed and automatically created, however for code readability it is a good idea to include it in both places.

Now when Mura CMS registers these objects, it will "auto-wire" these objects together. This makes it very much easier to work with these objects. For example:

// returns a single parent bean
var myparent = m.getBean('myParentBean').loadBy( myparentbeanid = 'x');
// returns a Mura Iterator of the children
var childreniterator = myparent.getMyChildrenIterator();
// returns a Mura Iterator of the children. Same as myparent.getMyChildrenIterator()
var children = myparent.getMyChildren();
// returns query object of the children
var rsChildren = myparent.getMyChildrenQuery();
// returns either a) a single child, if there is only one, or b) an array, if there are multiple children associated with it
var achild = m.getBean('myChildBean').loadBy( myparentid = '12345');
// returns the single associated myParentBean
var aparent = achild.getParentBean() 

You can also control automatic deletion of related beans by adding the following to the relationship property:

component extends="mura.bean.beanORM" entityname="myparentbean" table="my_myparentbean" {
 property name="myparentbeanid" fieldtype="id";
 property name="mychildren" fieldtype="one-to-many" relatesto="mychildbean" cascade="delete";
}

 

Variations & Options

In this case, when you delete the parent entity, all related children entities will also be deleted.

There is no limit on how many of these relationships a single object can have.

component extends="mura.bean.beanORM" entityname="myparentbean" table="my_myparentbean" {
 property name="myparentbeanid" fieldtype="id";
 property name="mychildren" fieldtype="one-to-many" relatesto="mychildbean";
 property name="anotherobject" fieldtype="one-to-one" relatesto="anotherobjectbean";
 property name="mygrandparents" fieldtype="many-to-one" relatesto="mygrandparentsbean";
}

It is important to note that, for these relationships to function, the objects must have a property identified as fieldtype="id". This identifies the unique key for each entity, and by default Mura uses this to bind them together. If you want to bind the objects together with an alternate id, you must identify that (note that this generally only applies to one-to-one relationships):

component extends="mura.bean.beanORM" entityname="myparentbean" table="my_myparentbean" {
 property name="myparentbeanid" fieldtype="id";
 property name="secondarystatus" fieldtype="one-to-one" relatesto="mystatusbean" fkcolumn="secondarystatusid";
}

You can of course include fkcolumn in all relevant cases, if you choose to. Just remember that when it is absent, Mura will assume the relationship is bound to the primary key aka "id" of the object.

One other very important point I should make is that you can also relate your custom Mura ORM objects to core Mura CMS objects:

component extends="mura.bean.beanORM" entityname="mychildbean" table="my_mychildbean" {
 property name="mychildbeanid" fieldtype="id";
 property name="user" fieldtype="one-to-one" relatesto="user";
 property name="content" fieldtype="one-to-one" relatesto="content";
}

In this example, each mychildbean would be associated to a single user and content entry. As you probably suspect, this can add a great deal of power and flexibility to your Mura CMS website.

 

Best Practice

Generally speaking, there are a few best practices that you should employ when working with Mura ORM objects.

  • Naming Conventions: as you can see in the above examples, I have prefixed all of my examples with "my". This is because I am working in an active namespace inside of Mura CMS. There are many core beans already at work in there, like user and content, and it would be dangerous for me to use names that might conflict with already-existing beans (including custom Mura ORM objects that might exist in other plugins, themes, etc.) A good practice is to use a standard prefix for all your custom beans. This will make them easier to identify and safer to deploy.
     
  • Locations: because your beans can be instantiated from numerous locations within Mura, it is wise to be consistent in where you place yours. For instance, if they are theme-specific, place your /model directory within the theme.
     
  • Clean Up: make consistent use of the cascade="delete" feature or you may find many detached child records hiding within your database

Finally, I suggest you review our online documentation on Mura ORM to further discover the wide variety of possibilities this tool offers.

 

Comments

Post a Comment

Required Field