How can I create a content type and associated fields programmatically?

I've googled and cannot locate any code examples.

Code or links to such, very welcomed.

 

 

 

 

Most helpful answers

@JDSaward - I'm sorry I didn't get to this question earlier, because I've been doing a lot of this kind of work lately. But, the answers you received are pretty good. I'll just point you (and future readers to more examples). 

As @docwilmot suggested, the standard install profile is the first place I look for things like this. 

Here is a discussion about creating nodes programatically:
https://forum.backdropcms.org/forum/how-does-one-create-node-programmati...

The Sample Animal Content module creates a content type and some content programmatically. 
https://github.com/backdrop-contrib/sample_animal_content

The FAQ Recipe module creates a content type using config files and then creates some nodes programmatically. 
https://github.com/backdrop-contrib/faq_recipe

I've created a few Recipe projects that create content types and nodes. These modules generally use config to create the content type. 
https://github.com/backdrop-contrib/testimonial_recipe
https://github.com/backdrop-contrib/user_guide_recipe

This module might also have some interesting ideas for you:
https://github.com/backdrop-contrib/welcome

NOTE: I'd love feedback or ideas for improvement on any of these projects.

Once you know how to do these things, you can help create more "recipe" modules. 
https://backdropcms.org/news/config-recipes-a-possible-path-forward

Hello, JDSaward, and thanks for posting!

One way to create a content type is to use the function node_type_save($info). According to the documentation of that function, you must pass the following information in $info (which must be an object):

The node type to save; an object with the following properties:
 *   - type: A string giving the machine name of the node type.
 *   - name: A string giving the human-readable name of the node type.
 *   - base: A string that indicates the base string for hook functions. For
 *     example, 'node_content' is the value used by the UI when creating a new
 *     node type.
 *   - description: A string that describes the node type.
 *   - help: A string giving the help information shown to the user when
 *     creating a node of this type.
 *   - custom: TRUE or FALSE indicating whether this type is defined by a module
 *     (FALSE) or by a user (TRUE) via Add Content Type.
 *   - modified: TRUE or FALSE indicating whether this type has been modified by
 *     an administrator. Currently not used in any way.
 *   - locked: TRUE or FALSE indicating whether the administrator can change the
 *     machine name of this type.
 *   - disabled: TRUE or FALSE indicating whether this type has been disabled.
 *   - has_title: TRUE or FALSE indicating whether this type uses the node title
 *     field.
 *   - title_label: A string containing the label for the title.
 *   - module: A string giving the module defining this type of node.
 *   - orig_type: A string giving the original machine-readable name of this
 *     node type. This may be different from the current type name if the locked
 *     field is 0.
 

You can create that object like so:

$info = new stdClass();
$info->type = 'my_type';
$info->name = 'My type';
etc.

and then invoke the function.

And, one way to associate fields is by using field_create_field() and field_create_instance(). For example:

 

  // Add field_piece_period
  $field = field_info_field('field_piece_period');
  $field_instance = field_info_instance('piece', 'field_piece_period', $piece_type->type);
  
  if (empty($field)) {
    $field = array(
      'translatable' => '0',
      'settings' => array(
        'allowed_values' => array(
          'Baroque' => 'Baroque',
          'Classical' => 'Classical',
          'Romantic' => 'Romantic',
          'Impressionistic' => 'Impressionistic',
          'Contemporary' => 'Contemporary',
        ),
        'allowed_values_function' => '',
      ),
      'field_name' => 'field_piece_period',
      'type' => 'list_text',
      'active' => '1',
      'locked' => '0',
      'cardinality' => '1',
      'required' =>FALSE,
    );
    field_create_field($field);
    
  }
   
  if (empty($field_instance)){
    $instance = array(
      'label' => 'Era',
      'widget' => array(
        'type' => 'options_select',
        'active' => 1,
        'settings' => array(),
      ),
      'settings' => array(
        'user_register_form' => FALSE,
      ),
      'display' => array(
        'default' => array(
          'label' => 'above',
          'type' => 'list_default',
          'settings' => array(),
          'weight' => 8,
        ),
      ),
      'required' => 1,
      'default_value' => NULL,
      'field_name' => 'field_piece_period',
      'entity_type' => 'piece',
      'bundle' => $piece_type->type,
    );
    field_create_instance($instance);  
  }

You can get all the values needed in the arrays above by creating a field manually through the UI first, and then inspecting the json file in your /files/config_XXXX/active folder, or by using Devel and running dpm(field_info_field('MYFIELD')); and dpm(field_info_instance(NODE, FIELD, TYPE));

In Backdrop it's also possible to do all of the above by creating the needed configuration json files directly, putting them into your custom module's config folder, and installing your module. That transfers those json files into the files/config_XXX/active folder. But I prefer the programmatic way I outlined above. :)

I hope that's helpful!

Comments

Hello, JDSaward, and thanks for posting!

One way to create a content type is to use the function node_type_save($info). According to the documentation of that function, you must pass the following information in $info (which must be an object):

The node type to save; an object with the following properties:
 *   - type: A string giving the machine name of the node type.
 *   - name: A string giving the human-readable name of the node type.
 *   - base: A string that indicates the base string for hook functions. For
 *     example, 'node_content' is the value used by the UI when creating a new
 *     node type.
 *   - description: A string that describes the node type.
 *   - help: A string giving the help information shown to the user when
 *     creating a node of this type.
 *   - custom: TRUE or FALSE indicating whether this type is defined by a module
 *     (FALSE) or by a user (TRUE) via Add Content Type.
 *   - modified: TRUE or FALSE indicating whether this type has been modified by
 *     an administrator. Currently not used in any way.
 *   - locked: TRUE or FALSE indicating whether the administrator can change the
 *     machine name of this type.
 *   - disabled: TRUE or FALSE indicating whether this type has been disabled.
 *   - has_title: TRUE or FALSE indicating whether this type uses the node title
 *     field.
 *   - title_label: A string containing the label for the title.
 *   - module: A string giving the module defining this type of node.
 *   - orig_type: A string giving the original machine-readable name of this
 *     node type. This may be different from the current type name if the locked
 *     field is 0.
 

You can create that object like so:

$info = new stdClass();
$info->type = 'my_type';
$info->name = 'My type';
etc.

and then invoke the function.

And, one way to associate fields is by using field_create_field() and field_create_instance(). For example:

 

  // Add field_piece_period
  $field = field_info_field('field_piece_period');
  $field_instance = field_info_instance('piece', 'field_piece_period', $piece_type->type);
  
  if (empty($field)) {
    $field = array(
      'translatable' => '0',
      'settings' => array(
        'allowed_values' => array(
          'Baroque' => 'Baroque',
          'Classical' => 'Classical',
          'Romantic' => 'Romantic',
          'Impressionistic' => 'Impressionistic',
          'Contemporary' => 'Contemporary',
        ),
        'allowed_values_function' => '',
      ),
      'field_name' => 'field_piece_period',
      'type' => 'list_text',
      'active' => '1',
      'locked' => '0',
      'cardinality' => '1',
      'required' =>FALSE,
    );
    field_create_field($field);
    
  }
   
  if (empty($field_instance)){
    $instance = array(
      'label' => 'Era',
      'widget' => array(
        'type' => 'options_select',
        'active' => 1,
        'settings' => array(),
      ),
      'settings' => array(
        'user_register_form' => FALSE,
      ),
      'display' => array(
        'default' => array(
          'label' => 'above',
          'type' => 'list_default',
          'settings' => array(),
          'weight' => 8,
        ),
      ),
      'required' => 1,
      'default_value' => NULL,
      'field_name' => 'field_piece_period',
      'entity_type' => 'piece',
      'bundle' => $piece_type->type,
    );
    field_create_instance($instance);  
  }

You can get all the values needed in the arrays above by creating a field manually through the UI first, and then inspecting the json file in your /files/config_XXXX/active folder, or by using Devel and running dpm(field_info_field('MYFIELD')); and dpm(field_info_instance(NODE, FIELD, TYPE));

In Backdrop it's also possible to do all of the above by creating the needed configuration json files directly, putting them into your custom module's config folder, and installing your module. That transfers those json files into the files/config_XXX/active folder. But I prefer the programmatic way I outlined above. :)

I hope that's helpful!

argiepiano Very good, Thank you. And good reminder that one pathway for me to consider is to put the config json files into the module.

What makes you prefer the programmatic way over the config way?

I prefer the programmatic way because you have more control over the different configuration settings (for example by using variables). With the json file, once you create it you have to either change things manually, or resort to the same kind of programmatic functions to make changes. Also because that's how I used to do it in Drupal.

@JDSaward - I'm sorry I didn't get to this question earlier, because I've been doing a lot of this kind of work lately. But, the answers you received are pretty good. I'll just point you (and future readers to more examples). 

As @docwilmot suggested, the standard install profile is the first place I look for things like this. 

Here is a discussion about creating nodes programatically:
https://forum.backdropcms.org/forum/how-does-one-create-node-programmati...

The Sample Animal Content module creates a content type and some content programmatically. 
https://github.com/backdrop-contrib/sample_animal_content

The FAQ Recipe module creates a content type using config files and then creates some nodes programmatically. 
https://github.com/backdrop-contrib/faq_recipe

I've created a few Recipe projects that create content types and nodes. These modules generally use config to create the content type. 
https://github.com/backdrop-contrib/testimonial_recipe
https://github.com/backdrop-contrib/user_guide_recipe

This module might also have some interesting ideas for you:
https://github.com/backdrop-contrib/welcome

NOTE: I'd love feedback or ideas for improvement on any of these projects.

Once you know how to do these things, you can help create more "recipe" modules. 
https://backdropcms.org/news/config-recipes-a-possible-path-forward

stpaultim Thank you. I'm working my way through all this, extending my understanding.