Eval Criteria # 4
How is content represented in the API?
We’ve talked about types and attributes as an editorial feature – they provide an optimized editorial interface, and they assist editors in ensuring the correct value is stored. So far, this has been about writing content.
However, what about the other side? How can types and attributes help when reading content? When integration or template developers need to retrieve a content object to work with it, what data construct do they actually get back from the repository?
Object Oriented Programming
First, a programming primer.
Most programming languages today are object oriented (“object oriented programming” is somewhat hilariously abbreviated as “OOP”). This style of programming means units of code logic are defined in structures called “classes,” from which you can create “objects.” Objects expose data in “properties” and can perform actions called “methods.”
The benefit of OOP is you can work with an object – tell it to do things and ask it for information – without having to know what it’s doing inside to get any of the work done.
For example, you might have an object called “War and Peace” which is created from the Book
class. You can ask “War and Peace” for its Title
or tell it to do something like TurnThePage
and expect responses from it, but you have no idea how it’s doing any of this. The actual code to do this is internal to the Book class.
The external structures that are exposed to you – the Title
property or the TurnThePage
method – are known as the interface. The code inside of the class that actually performs the required processing is known as the implementation. A key benefit of OOP is you don’t have to concern yourself with the implementation, so long as you know what to expect when you invoke different aspects of the interface.
This is generally considered a clean and helpful way to manage code. If I have a Book
object anywhere in my codebase, I can trust it will have the same interface, without me having to scatter or duplicate code everywhere. If I want to change how it does something, I can change the implementation inside the class and trust that change will be reflected everywhere it’s used.
The parallels to content management should be very clear –
- A content type is a Class
- A content object is an Object
- An attribute is a Property
Thus, it’s very helpful if content objects can be automatically translated into code objects when they’re retrieved. In your programming code, you can have a Book
class with a property for Title
and know it will automatically retrieve the correct attribute value.
This is called strong typing, which means there’s a parallel between the content type and a class in the programming language, and the CMS will handle the translation automatically.
Some systems don’t strongly type content objects, and they are all represented as a generic, catch-all class – so-called weak typing – when developers need to work with them.
Instead of Book
, the class might be called something like ContentObject
or Item
. Instead of reading named properties on the object like Title
, there is some generic method for accessing attributes, like GetAttribute("Title")
.
This is not a major problem, it just means more work for the developer, and a slightly increased chance of error if there’s an invalid attribute value.
Attribute Typing
Strong-typing isn’t just for objects. Attribute values can be strongly-typed as well.
In the last chapter, we discussed how an attribute’s logical value is serialized into a primitive value for storage. The resulting primitive value – the string of XML in the example – is easy to store in a generalized datastore, but it isn’t ideal for developers. They’d rather get that in some native programming construct of whatever language they’re working in.
Depending on the system, the value stored in the attribute might be accessible as a strongly typed object, meaning it’s accessed as a true representation of the data stored in it, in whatever programming or templating language the CMS uses.
To take the map example, you might deserialize that back into a helpful object value for your template developers to use (JavaScript/JSON, in this example):
{ latitude: 43.55, longitude: -96.7, zoom: 1.5 }
This allows the deserialization to be consistent, so not everyone needing to work with the data is creating their own method to make sense of the primitive value. This clearly makes life easier for the developers charged with integrating and templating the system.
Programming Logic
In addition to simply reading data values, it’s sometimes helpful to have other code logic available from a class to make the underlying content object easier to work with.
For example, let’s say you have a Chapter with an attribute for Title. There’s also an attribute called Header, to represent a shortened title to print at the top of each page of that chapter. In most cases, the Header will be explicitly entered as a shorter version of Title. However, in some cases, Title is already short enough, so you just want to use it instead of entering an explicit Header.
This is simple enough to code in almost any programming language. Here it is as a class property in C#:
HeaderDisplay => Header ?? Title;
The ??
operator is called a “null coalesce,” and says, “if there’s nothing in the value to the left of me, return what’s to the right of me instead.”
In this example, HeaderDisplay becomes a pseudo-attribute with no actual, entered value of its own. Instead, it performs some logical processing to return the value of Header if that’s defined, and Title if it’s not.
Note this has nothing to do with the CMS. Everything above is in C#, which is an underlying programming language. The CMS has no idea this is being done, but this functionality is enabled by the CMS’s ability to automatically deserialize a Chapter content object into a Chapter
class in C#, onto which we can add helpful code.
If you can’t do this, then the functional goal above is still possible, your developers just have to write this code in other ways, and for each context. For example, your template developer might have to write a special template function in whatever templating language they’re using. And your back-end developer might have to duplicate the above logic in some other method that’s accessible to their code.
This chapter was a bit of a stretch from the core discussion of content modeling, but it matters because a strongly-typed API enables you to perform pseudo-modeling operations in your code.
The concept of “failing over” from Header to Title is logic that belongs to Chapter, and if you have access to the full array of features in your underlying language, you can keep this logic clean and centralized. We have essentially created a new attribute – HeaderDisplay – in code, rather than as actual content. The final, effective value originates in logic, not data.
This goes back to the core principle of maintainability – not so much of your specific content model, but of the underlying code in particular, and the entire integration effort in general.
Evaluation Questions
- When retrieving content for integration or templating, what data structure is a content object represented as in code?
- How can calculated attributes or other helper code be represented on objects for usage in templating or integration?