Learn how Scrivito CMS can help you deliver amazing digital experiences
See Scrivito CMS in action

Maintaining CMS Object Classes with Migrations

Maintaining CMS Object Classes with Migrations

This article refers to Scrivito SDK versions up to 0.50. From version 0.60 onwards, object classes are maintained solely as Ruby models. The CMS is no longer aware of object classes, leaving it to the Ruby models to specify their attributes.

As a side benefit, migrations for maintaining object classes in the CMS are not required. Simply have the models generated, then equip them with the desired attributes.

Some of the generators the SDK provides create migration files like the ones given here as examples. For details regarding the Scrivito SDK Migrations API, please refer to the Ruby SDK documentation on object classes.

Creating an object class

In the following example, we will define an object class named ContentPage. This object class should have a title attribute that holds the page title, and a body attribute for storing the HTML page content. After creating this object class, all objects based on it have these attributes, and individual values can be assigned to them.

$ rails g scrivito:migration create_content_page 

This creates a file, scrivito/migrate/*_create_content_page.rb. Open the file in your text editor and replace the example code with the actual migration code:

class CreateContentPage < ::Scrivito::Migration def up Scrivito::ObjClass.create( name: 'ContentPage', attributes: [ {name: 'title', type: 'string'}, {name: 'body', type: 'html'} ] ) end end

Running this migration with rake scrivito:migrate creates the object class in the rtc working copy. Finally, publish this working copy using rake scrivito:migrate:publish.

Adding an attribute to an object class

In this example, the name of the attribute to be added to an object class is new_html, and its type is html. Attributes are local to individual object classes, i.e. all object classes can have their own new_html attribute of any type. The new attribute will be added to ContentPage only.

$ rails g scrivito:migration add_new_html_to_content_page

Open the generated migration file in your text editor and replace the example code in the up method with the actual migration code.

class AddNewHtmlToContentPage < ::Scrivito::Migration def up Scrivito::ObjClass.find('ContentPage').attributes.add(name: 'new_html', type: 'html') end end

After running this migration, the object class has got the new attribute. Finally, publish the rtc working copy.

Removing an attribute from an object class

class RemoveObsoleteAttributeFromContentPage < ::Scrivito::Migration def up Scrivito::ObjClass.find('ContentPage').attributes.delete('my_obsolete_attribute') end end

This removes the attribute named my_obsolete_attribute from the ContentPage object class. Since attributes are local to an object class, the attribute will only be removed from the specified object class.

Renaming an attribute

Renaming an attribute consists of three steps:

  1. Add the attribute using a new attribute name.
  2. Copy all attribute values from the old attribute of every object of the object class to the new attribute.
  3. Remove the old attribute.

Example:

$ rails g scrivito:migration rename_content_page_description_to_summary
class RenameContentPageDescriptionToSummary < ::Scrivito::Migration def up Scrivito::ObjClass.find('ContentPage').attributes.add(name: 'summary', type: 'html') ContentPage.all.order(:_path).batch_size(100).each do |page| page.update(summary: page.description) end Scrivito::ObjClass.find('ContentPage').attributes.delete('description') end end

This example assumes that you already have a description attribute in the ContentPage object class. It adds the new summary attribute to the object class, then copies description from every content page to the new summary attribute. To do so, the migration uses the search engine facility to iterate over all these pages.

The order clause ensures that we get a deterministic search result over multiple subsequently executed searches if we have more than batch_size pages in the CMS.

Changing the type of an attribute

Changing the type of an attribute consists of the following steps:

  1. Collect the current attribute values, and migrate them on-the-fly in memory.
  2. Change the type of the attribute.
  3. Store the collected data back to the attribute.

As an example, we want to change the type of the source attribute in ImageWidgets from linklist to reference.

class ConvertImageWidgetLinklistToReference < ::Scrivito::Migration def up  obj_widget_data = {}  objs = Obj.where(:_obj_class, :equals, "ImageWidget").order(:_path).batch_size(100) objs.each do |obj| obj.all_widgets_from_pool.each do |widget| next if widget.obj_class.name != "ImageWidget" image_link = widget["source"].first if image_link obj_widget_data[obj.id] ||= {} obj_widget_data[obj.id][widget.id] = { source: image_link.obj.id } end end end Scrivito::ObjClass.find('ImageWidget').attributes.delete('source') Scrivito::ObjClass.find('ImageWidget').attributes.add(name: 'source', type: 'reference') obj_widget_data.each do |obj_id, widget_pool| widget_pool.each do |widget_id, attributes| Obj.find(obj_id).widget_from_pool(widget_id).update(attributes) end  end end end

Creating a CMS object

$ rails g scrivito:migration create_products_page

If an object class such as ContentPage already exists, you can create objects based on this class from within your migrations.

class CreateProductsPage < ::Scrivito::Migration def up Obj.create( _path: "/websites/en/products", _obj_class: "ContentPage", title: "Our Products", ) end end

_path and _obj_class are mandatory fields. Other attributes such as title in the example above may be specified as well.

Deleting an object

$ rails g scrivito:migration delete_obsolete_products_page
class DeleteObsoleteProductsPage < ::Scrivito::Migration def up Obj.find_by_path('/websites/en/products').destroy end end

Changing the object class of an object

Suppose you want to introduce a new object class and need to assign it to already existing objects. In the example below, the object with the path /websites/en/contentpage presently has the ContentPage object class. Since the object now needs to function differently, as a special navigation group node, an appropriate new object class, NavigationGroup in this case, needs to be assigned to it:

class TurnContentPageIntoNavigationGroup < ::Scrivito::Migration def up Obj.find_by_path('/websites/en/contentpage').update(_obj_class: NavigationGroup) end end