If you’re a new Drupal user, you may have already created a fresh content type manually to organize your site correctly. But what happens if you want to create this content type with your module installation and don’t want to do it manually each type you install it on a new website ?. Here is how to create a content type programmatically with Drupal 7.
Begin with a new array containing custom properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$t = get_t(); $my_custom_type = array( 'type' => 'my_custom_type', 'name' => $t('My Custom Type'), // 'base' tells Drupal the base string for hook functions. // This is often the module name; if base is set to 'mymodule', Drupal // would call mymodule_insert() or similar for node hooks. // In this case, we set base equal to 'node_content' so Drupal will handle // our node as if we had designed it in the UI. 'base' => 'node_content', 'description' => $t('This content will display one type element.'), 'title_label' => $t('Title'), 'custom' => TRUE, ); |
Complete the content type definition by setting any defaults not explicitly declared above with node_type_set_defaults method.
1 |
$node_custom_type = node_type_set_defaults($my_custom_type); |
We add a body field and set the body label immediately.
1 |
node_add_body_field($node_custom_type, $t('Description')); |
Save the content type
1 |
node_type_save($node_custom_type); |
If you want to modify some body display settings, load the instance definition from the existing content type’s body with field_info_instance
1 |
$body_instance = field_info_instance('node', 'body', 'my_custom_type'); |
Now you can modify the array settings, an example: Instructing the body to display as a summary
1 |
$body_instance['settings']['display_summary'] = FALSE; |
Save our changes to the body field instance. field_update_instance
1 |
field_update_instance($body_instance); |
The hardest part will be to define and add all fields and instances to the content type. If you have lots of fields, I would give advice to add them in a foreach loop like this:
1 2 3 4 5 6 7 8 9 10 11 |
// Create all the fields we are adding to our content type. // http://api.drupal.org/api/function/field_create_field/7 foreach (_custom_type_installed_fields() as $field) { field_create_field($field); } // Create all the instances for our fields. // http://api.drupal.org/api/function/field_create_instance/7 foreach (_custom_type_installed_instances() as $instance) { field_create_instance($instance); } |
You have to understand the difference between field_create_field and field_create_instance. The first will create a new table in the database and contains how the data is saved into db, the second will be based on an existing field linked to the content type and contains display settings.
So if you create new fields all the time instead of reusing existing ones, you’re going to have a big number of db tables automatically created by Drupal. Then you should try reusing whenever you can all existing fields to simply create links between the field and your new content type. You can use only one time a field for each content type.
Example of the two function implementations:
- node_test_filter is a taxonomy based selection field
- node_test_text is a textarea field
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
function _custom_type_installed_fields() { $t = get_t(); return array( 'node_test_filter' => array( 'field_name' => 'node_test_filter', 'label' => $t('Select filters'), 'type' => 'list_text', 'cardinality' => '-1', // change this to 1 to get radio buttons instead 'foreign keys' => array(), 'indexes' => array( 'value' => array( 0 => 'value', ), ), 'module' => 'list', 'settings' => array( 'allowed_values_function' => '_custom_filters_options_list', ) ), 'node_test_text' => array( 'field_name' => 'node_test_text', 'cardinality' => 1, 'type' => 'text_long', ), ); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
function _custom_type_installed_instances() { $t = get_t(); return array( 'node_test_filter' => array( 'field_name' => 'node_test_filter', 'label' => $t('Filters'), 'description' => $t('Select associated filters'), 'default_value' => NULL, // add a default value here that matches your key => index values 'display' => array( 'default' => array( 'label' => 'above', 'module' => 'list', 'settings' => array(), 'type' => 'list_default', 'weight' => -1, ), 'teaser' => array( 'label' => 'above', 'settings' => array(), 'type' => 'hidden', 'weight' => -1, ), ), 'required' => 1, 'settings' => array( 'user_register_form' => FALSE, ), 'widget' => array( 'active' => 1, 'module' => 'options', 'settings' => array(), 'type' => 'options_buttons', 'weight' => '-1', ) ), 'node_test_text' => array( 'field_name' => 'node_test_text', 'label' => $t('Info'), 'type' => 'text', 'widget' => array( 'type' => 'text_textarea', 'settings' => array('rows' => 5), ), 'settings' => array( 'text_processing' => 1, ), 'entity_type' => 'node', 'bundle' => 'my_custom_type', ), ); } |
FYI – The code should go into /mymodule/mymodule.install file, not into *.module.
Thank you for bringing more accuracy !
this code not working
This is correct, you just don’t understand how to use it.
Your code missinga function _custom_filters_options_list
I don’t see the need here. Tell me ?
For example if you need a content type to be created by a module on multiple drupal installation.
You don’t want to create everywhere the same content type, even more if there are lots of fields to add.