初探drupal8

下载安装drupal8版本,新版本的变化:

1、管理界面的变化。

顶部的导航更加简洁和方便,而且可以很方便的折叠和打开,打开以后单独显示在左侧区域。

2、模块管理名称更新为extend.

3、默认的内容类型为article,不再支持blogs模块.

袁推荐的Drupal 8 亮点:
1)使用Symfony框架翻新内核;
2)新增配置管理系统;
3)HTML5表单元素和响应式;
4)移动端友好的管理工具栏;
5)优化内置翻译机制;
6)基于Twig的模版引擎;
7)Views加入内核;
8)新的区块、布局管理机制;
9)所见即所得编辑器及行内编辑;
10)Date、Pathauto、Profile2等模块加入核心
……
 
 
附件尺寸
Image icon admin.jpg29.12 KB

drupal8下载

drupal8下载

Drupal8 :https://www.drupal.org/node/3060/release?api_version[]=7234

打开这个页面,下载最新的Drupal 8 版本。

Taxonomy upgrade extras: 

drupal8变量

drupal8里面更新了哪些变量,改如何使用呢?learning带你走人drupal8神奇的世界。

drupal8变量的存储和设定使用yml文件

drupal8变量的存储和设定使用yml文件

variable_get()/variable_set()替换为get()/set()

<?php
// Load a set of configuration out of the active store.
// 'node.settings' refers to the filename of the .yml file, without the extension.
//导入
$config = config('node.settings');
// Access a single value out of the store.
echo $config->get('items_per_page');
// Change a value and save it back to both the active store and the filesystem.
//--改变和保存
$config->set('items_per_page','20');
$config->save();
?>

 

config 文件存储目录

config/node.settings.yml 文件存储内容

<?php
items_per_page
: '10'
?>

详细例子参考:

http://heyrocker.com/how-use-drupal-8-configuration-system

drupal8用户常量的定义方法

常量 const

在类里面定义常量用 const 关键字,而不是通常的 define() 函数。

<?php
/**
 * @file
 * Enables the user registration and login system.
 */

/**
 * Maximum length of username text field.
 * 用户名最大长度
 */
const USERNAME_MAX_LENGTH = 60;

/**
 * Maximum length of user e-mail text field.
 * 邮件地址最大长度
 */
const EMAIL_MAX_LENGTH = 254;

/**
 * Only administrators can create user accounts.
 * 只有管理员可以创建用户账户
 */
const USER_REGISTER_ADMINISTRATORS_ONLY = 'admin_only';

/**
 * Visitors can create their own accounts.
 * 访问者可以创建他们的账户
 */
const USER_REGISTER_VISITORS = 'visitors';

/**
 * Visitors can create accounts, but they don't become active without
 * administrative approval.
 * 访问者可以创建,但需要管理员审核
 */
const USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL = 'visitors_admin_approval';
?>

drupal8的$_GET['q']已经移除

你是不是已经习惯使用$_GET['q']来获取页面传递的路径了?

$_GET['q']在drupal8里已经移除,换成current_path()

drupal_access_denied()和drupal_not_fount()替换

drupal_access_denied()替换为 AccessDeniedHttpException()

drupal_not_fount()替换为 NotFoundHttpException();

drupal7没有权限

drupal_access_denied();
drupal_exit();

drupal8替换为

throw new AccessDeniedHttpException();

drupal7没有找到

drupal_not_found();
drupal_exit();

drupal8替换为

throw new NotFoundHttpException();

别忘了调用类库:

use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

drupal8数据库查询

在drupal8的数据库查询里面发生了些什么变化呢?learning将一步步带你揭开drupal8的神秘面纱。

drupal8查询添加分页代码变化

1、添加扩展路径

2、调用yml存储的分页

    ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
    ->limit(config('node.settings')->get('items_per_page'))

详细代码如下:

<?php
  $select
= db_select('node', 'n')
    ->
fields('n', array('nid', 'sticky', 'created'))
    ->
condition('n.promote', 1)
    ->
condition('n.status', 1)
    ->
orderBy('n.sticky', 'DESC')
    ->
orderBy('n.created', 'DESC')
    ->
extend('Drupal\Core\Database\Query\PagerSelectExtender')
    ->
limit(config('node.settings')->get('items_per_page'))
    ->
addTag('node_access');
?>

drupal8目录结构

drupal8目录结构发生了变化,自定义的modules目录被放到了根目录,核心的modules模块放到了core下面。

每个模块下面根据需求添加lib目录和config目录

config目录存放yml文件

lib目录存放扩展的类库和函数

附件尺寸
Image icon dir.jpg14.01 KB

drupal8几个核心控制类库位置

 

几个核心控制类库位置
 
'entity class' => 'Drupal\Core\Entity\Entity', 
'controller class' => 'Drupal\Core\Entity\DatabaseStorageController', 
'list controller class' => 'Drupal\Core\Entity\EntityListController', 
'render controller class' => 'Drupal\Core\Entity\EntityRenderController', 
'form controller class' => array(
  'default' => 'Drupal\Core\Entity\EntityFormController',
), 

drupal8函数和类库的存放

EntityTest.php文件简单分析:

定义一些可以调用的变量和结构函数:

位置:

core\modules\system\tests\modules\entity_test\lib\Drupal\entity_test\Plugin\Core\Entity

针对模块的相对存放路径:lib\Drupal\entity_test\Plugin\Core\Entity

需要了解的基础知识:

core\lib目录: 存放整个网站可以调用的类库和函数

modulename\lib目录: 某个模块调用的类库和函数

<?php
//--在文件开头使用以下命名方法定义当前类库函数的路径:
namespace Drupal\entity_test\Plugin\Core\Entity;
//--调用需要使用到的核心的类库目录
use Drupal\Core\Entity\EntityNG;
use
Drupal\Core\Annotation\Plugin;
use
Drupal\Core\Annotation\Translation;
?>

具体内容可以查看api:

drupal8后台管理菜单更方便

drupal8在顶部添加一个menu菜单,点击后可以显示管理相关操作

 

附件尺寸
Image icon menu.jpg21.69 KB

drupal8核心views目录

core\modules\views\lib\Drupal\views\Plugin\views打开这个目录,你会发现很多子目录

 

 

附件尺寸
Image icon views1.jpg22.76 KB

drupal8工具条编辑菜单等界面

drupal8工具条编辑菜单等界面发生的变化:

http://www.ostraining.com/blog/drupal/drupal-8-december/

drupal8相关测试

http://groups.drupal.org/node/276518

测试d8安装时语言载入自动化

drupal8表单

drupal8表单相关

comment_form_node_form_alter两个变化

comment_form_node_form_alter两个变化:

1、$node的获取

drupal8

  $node = $form_state['controller']->getEntity($form_state);
 

drupal7

  $node = $form['#node'];
 

2、js的添加

drupal8

    '#attached' => array(
      'library' => array(array('comment', 'drupal.comment')),
    ),

drupal7

    '#attached' => array(
      'js' => array(drupal_get_path('module', 'comment') . '/comment-node-form.js'),
    ),

具体代码参考api

function comment_form_node_form_alter(&$form, $form_state) {

}

drupal8列表的定义和显示方法


drupal8使用以下方法单独定义列表头和内容在CategoryListController.php文件

<?php

/**
 * Definition of Drupal\contact\CategoryListController.
 */

namespace Drupal\contact;

use
Drupal\Core\Config\Entity\ConfigEntityListController;
use
Drupal\Core\Entity\EntityInterface;

/**
 * Provides a listing of contact categories.
 */
class CategoryListController extends ConfigEntityListController {

 
/**
   * Overrides Drupal\Core\Entity\EntityListController::buildHeader().
   */
 
public function buildHeader() {
   
$row['category'] = t('Category');
   
$row['recipients'] = t('Recipients');
   
$row['selected'] = t('Selected');
   
$row['operations'] = t('Operations');
    return
$row;
  }

 
/**
   * Overrides Drupal\Core\Entity\EntityListController::buildRow().
   */
 
public function buildRow(EntityInterface $entity) {
   
$row['category'] = check_plain($entity->label());
   
$row['recipients'] = check_plain(implode(', ', $entity->recipients));
   
$default_category = config('contact.settings')->get('default_category');
   
$row['selected'] = ($default_category == $entity->id() ? t('Yes') : t('No'));
   
$row['operations']['data'] = $this->buildOperations($entity);
    return
$row;
  }

}
?>

调用方法,是不是很简洁呢:

<?php
/**
 * Page callback: Lists contact categories.
 *
 * @return array
 *   A build array in the format expected by drupal_render().
 *
 * @see contact_menu()
 */
function contact_category_list() {
 
drupal_set_title(t('Contact form categories'));
  return
entity_list_controller('contact_category')->render();
}
?>
附件尺寸
Image icon list.jpg14.36 KB

drupal8更改已有表单hook_form_FORM_ID_alter()

1、filedset更新为details

2、使用yml方式保存变量

<?php
/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Add the default personal contact setting on the user settings page.
 *
 * @see user_admin_settings()
 */
function contact_form_user_admin_settings_alter(&$form, &$form_state) {
 
$form['contact'] = array(
   
'#type' => 'details', //--原来使用fieldset
   
'#title' => t('Contact settings'),
   
'#weight' => 0,
  );
 
$form['contact']['contact_default_status'] = array(
   
'#type' => 'checkbox',
   
'#title' => t('Enable the personal contact form by default for new users.'),
   
'#description' => t('Changing this setting will not affect existing users.'),
   
'#default_value' => config('contact.settings')->get('user_default_enabled'),
  );
 
// Add submit handler to save contact configuration.
 
$form['#submit'][] = 'contact_form_user_admin_settings_submit';
}

/**
 * Form submission handler for user_admin_settings().
 *
 * @see contact_form_user_admin_settings_alter()
 */
//--新的保存变量的方法
function contact_form_user_admin_settings_submit($form, &$form_state) {
 
config('contact.settings')
    ->
set('user_default_enabled', $form_state['values']['contact_default_status'])
    ->
save();
}
?>

drupal8表单定义的变化

drupal8表单定义的变化:

扩展基础表单类EntityFormControllerNG来自定义表单

别忘了定义位置和调用核心类库

<?php
/**
 * @file
 * Definition of Drupal\entity_test\EntityTestFormController.
 */
namespace Drupal\entity_test;

use
Drupal\Core\Entity\EntityInterface;
use
Drupal\Core\Entity\EntityFormControllerNG;

class
EntityTestFormController extends EntityFormControllerNG {

public function
form(array $form, array &$form_state, EntityInterface $entity) {
   
//--继承父表单
   
$form = parent::form($form, $form_state, $entity);
   
//--相关语言的扩展
   
$langcode = $this->getFormLangcode($form_state);
   
$translation = $entity->getTranslation($langcode);

   
$form['name'] = array(
     
'#type' => 'textfield',
     
'#title' => t('Name'),
     
'#default_value' => $translation->name->value,
     
'#size' => 60,
     
'#maxlength' => 128,
     
'#required' => TRUE,
     
'#weight' => -10,
    );

   
$form['user_id'] = array(
     
'#type' => 'textfield',
     
'#title' => 'UID',
     
'#default_value' => $translation->user_id->value,
     
'#size' => 60,
     
'#maxlength' => 128,
     
'#required' => TRUE,
     
'#weight' => -10,
    );

   
$form['langcode'] = array(
     
'#title' => t('Language'),
     
'#type' => 'language_select',
     
'#default_value' => $entity->language()->langcode,
     
'#languages' => LANGUAGE_ALL,
    );

    return
$form;
  }
}
 
?>

 

调用显示表单的方法:

 

  <?php
 
/**
 * Menu callback: displays the 'Add new entity_test' form.
 *
 * @return array
 *   The processed form for a new entity_test.
 *
 * @see entity_test_menu()
 */
function entity_test_add() {
 
drupal_set_title(t('Create an entity_test'));
 
$entity = entity_create('entity_test', array());
  return
entity_get_form($entity);
}
?>
附件尺寸
Image icon entity_form.jpg15.07 KB

drupal彩蛋--寻找drupal examples

在drupal8的大本营里面发现了这个彩蛋,drupal8 examples隐藏的可够深的,不过还是被我给找到了。

目录:core\modules\system\tests\modules

模块默认的info文件里设定了隐藏属性:

hidden = TRUE

想做测试的朋友在前面添加分号(;),去掉改模块的隐藏属性,在extend下面就可以看到了。

还等什么?快开始你的测试之旅吧?

附件尺寸
Image icon example.jpg10.16 KB

drupal8无权限的页面

访问注册页面时出现以下提示:

You are not authorized to access this page.

查看module文件发现了里面的以下写法,不知道正式版本里面的定义方法,以及如何开启用户注册页面。

该方法可以适应于不想让用户访问的页面

  <?
  $items['user/register'] = array(
    'title' => 'Create new account',
    'type' => MENU_LOCAL_TASK,
    // @todo This route is now declared in user.routing.yml, so the below are
    //   only needed for drupal_valid_path(). Without a non-empty (but not
    //   necessarily valid) page callback, _menu_router_build() overrides the
    //   access callback to 0. Remove once drupal_valid_path works with the new
    //   routing system: http://drupal.org/node/1793520.
    'page callback' => 'NOT_USED',
    'access callback' => 'user_register_access',
  );
?>

折腾了一番,可以显示注册页面了,但还是不确定是如何开启的。

drupal模块开发分析

 

drupal模块开发常见问题:
 
1、一般的模块分析方法:安装后先分析menu
根据menu查询函数,menu的类型大概分为:
menu_default_local_task()默认显示tab
menu_local_task这种的显示在右侧tab
(tab显示需要最少两个才会显示)
 
menu_default_item()左侧菜单,一般可以不写
menu_suggested_item() 建议菜单,可以开启的
menu_callback()回调菜单,仅用于调用
 
menu调用的文件一般是page.inc/admin.inc
 
page.inc定义的函数一般是显示在客户端的
admin.inc定义的函数一般是管理页面的
 
路径判断:
包含admin/的需要有管理权限的才可以看到
 
 
2、定义模块:info/module两个文件就定义了
 
如果需要默认其他模块可以直接调用的函数放在.module文件里面,如果只是自己内部调用的,可以放到page/admin/other你自己名称的.inc文件里面
 
3、加载js/css全局使用,就放到hook_init函数里面
 
4、drupal_get_form()代表调用了表单
一般表单都是配对的
hook_form/hook_form_validate/hook_form_submit
 
 
5、如果是管理的页面一般还有个search
search一般也是单独的表单,如果执行完跳转drupal_goto()
 
6、drupal_set_message()输出信息
调试的时候也可以使用
 
7、用户没有权限的时候:
    drupal_access_denied();
    drupal_exit();
 
8、没有找到的时候:
      drupal_not_found();
      drupal_exit();
 
9、drupal7版本的模板引擎是什么?
phptemplate的模板引擎
写法html+php的混合写法,命名方法:tpl.php
 
10、comment.tokens.inc,带.inc的文件时什么意思
是可以被执行调用的php文件

 

autocomplete定义和使用

 

添加一个term reference的自动完成字段

<?php
//--添加字段
function taxonomy_field_widget_info() {
  return array(
   
'taxonomy_autocomplete' => array(
     
'label' => t('Autocomplete term widget (tagging)'),
     
'field types' => array('taxonomy_term_reference'),
     
'settings' => array(
       
'size' => 60,
       
'autocomplete_path' => 'taxonomy/autocomplete', //--调用路径
     
),
     
'behaviors' => array(
       
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
      ),
    ),
  );
}

//--定义路径
 
$items['taxonomy/autocomplete'] = array(
   
'title' => 'Autocomplete taxonomy',
   
'page callback' => 'taxonomy_autocomplete', //--调用函数
   
'access arguments' => array('access content'),
   
'type' => MENU_CALLBACK,
   
'file' => 'taxonomy.pages.inc',
  );

//--获取数据
function taxonomy_autocomplete($field_name, $tags_typed = '') {
 
// If the request has a '/' in the search text, then the menu system will have
  // split it into multiple arguments, recover the intended $tags_typed.
 
$args = func_get_args();
 
// Shift off the $field_name argument.
 
array_shift($args);
 
$tags_typed = implode('/', $args);

 
// Make sure the field exists and is a taxonomy field.
 
if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') {
   
// Error string. The JavaScript handler will realize this is not JSON and
    // will display it as debugging information.
   
print t('Taxonomy field @field_name not found.', array('@field_name' => $field_name));
    exit;
  }

 
// The user enters a comma-separated list of tags. We only autocomplete the last tag.
 
$tags_typed = drupal_explode_tags($tags_typed);
 
$tag_last = drupal_strtolower(array_pop($tags_typed));

 
$matches = array();
  if (
$tag_last != '') {

   
// Part of the criteria for the query come from the field's own settings.
   
$vids = array();
   
$vocabularies = taxonomy_vocabulary_get_names();
    foreach (
$field['settings']['allowed_values'] as $tree) {
     
$vids[] = $vocabularies[$tree['vocabulary']]->vid;
    }

   
$query = db_select('taxonomy_term_data', 't');
   
$query->addTag('translatable');
   
$query->addTag('term_access');

   
// Do not select already entered terms.
   
if (!empty($tags_typed)) {
     
$query->condition('t.name', $tags_typed, 'NOT IN');
    }
   
// Select rows that match by term name.
   
$tags_return = $query
     
->fields('t', array('tid', 'name'))
      ->
condition('t.vid', $vids)
      ->
condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE')
      ->
range(0, 10)
      ->
execute()
      ->
fetchAllKeyed();

   
$prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';

   
$term_matches = array();
    foreach (
$tags_return as $tid => $name) {
     
$n = $name;
     
// Term names containing commas or quotes must be wrapped in quotes.
     
if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
       
$n = '"' . str_replace('"', '""', $name) . '"';
      }
     
$term_matches[$prefix . $n] = check_plain($name);
    }
  }

 
drupal_json_output($term_matches);//--json格式的数据返回
}
?>

 

附件尺寸
Image icon autocmplete.jpg12.33 KB

drupal菜单定义

菜单定义函数hook_menu()

<?php
/**
 * Implements hook_menu().
 */
function contact_menu() {
 
$items['admin/structure/contact'] = array(
   
'title' => 'Contact form',
   
'description' => 'Create a system contact form and set up categories for the form to use.',
   
'page callback' => 'contact_category_list', //列表
   
'access arguments' => array('administer contact forms'),
   
'file' => 'contact.admin.inc', //定义管理界面调用函数的文件
 
);
 
$items['admin/structure/contact/add'] = array(
   
'title' => 'Add category',
   
'page callback' => 'drupal_get_form', //--调用表单
   
'page arguments' => array('contact_category_edit_form'), //--表单名称
   
'access arguments' => array('administer contact forms'),
   
'type' => MENU_LOCAL_ACTION, //7以后新增加的,描述作用于父菜单的动作,呈现动作连接
   
'weight' => 1,
   
'file' => 'contact.admin.inc',
  );
   
//--%contact对应contact_load,默认加载
    //--编辑的时候对应一个数字
 
$items['admin/structure/contact/edit/%contact'] = array(
   
'title' => 'Edit contact category',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('contact_category_edit_form', 4),
   
'access arguments' => array('administer contact forms'),
   
'file' => 'contact.admin.inc',
  );
 
$items['admin/structure/contact/delete/%contact'] = array(
   
'title' => 'Delete contact',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('contact_category_delete_form', 4),
   
'access arguments' => array('administer contact forms'),
   
'file' => 'contact.admin.inc',
  );
 
$items['contact'] = array(
   
'title' => 'Contact',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('contact_site_form'),
   
'access arguments' => array('access site-wide contact form'),
   
'type' => MENU_SUGGESTED_ITEM, //--建议类型的菜单,可以开启
   
'file' => 'contact.pages.inc',
  );
 
$items['user/%user/contact'] = array(
   
'title' => 'Contact',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('contact_personal_form', 1), //--1代表传递的用户id(%user所在的位置)
   
'type' => MENU_LOCAL_TASK, //--tab,显示在用户账户管理界面
   
'access callback' => '_contact_personal_tab_access',//--私有的自定义函数
   
'access arguments' => array(1),
   
'weight' => 2, //--权重,可以调整位置
   
'file' => 'contact.pages.inc',
  );
  return
$items;
}
?>

inc文件的用法

inc文件是可以调用的php文件.

用来定义函数

inc文件的用法:

1、导入的方法module_load_include();

<?php
 
// Load node.admin.inc from the node module.
 
module_load_include('inc', 'node', 'node.admin');
 
// Load content_types.inc from the node module.
 
module_load_include('inc', 'node', 'content_types');
?>

2、菜单调用的方法

<?php
  $items
['admin/content'] = array(
   
'title' => 'Content',
   
'description' => 'Find and manage content.',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('node_admin_content'),
   
'access arguments' => array('access content overview'),
   
'weight' => -10,
   
'file' => 'node.admin.inc',
  );
?>

在自定义模块里面调用核心模块文件


<?php
$items
['contact'] = array(
   
'title' => 'Contact',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('contact_site_form'),
   
'access arguments' => array('access site-wide contact form'),
   
'type' => MENU_SUGGESTED_ITEM, //--建议类型的菜单,可以开启
   
'file' => 'contact.pages.inc',
   
'file path'=>drupal_get_path('module','contact'),//--在其他模块里面调用的时候添加路径
 
);
?>

清除用户权限缓存

<?php
 
// Clear the user access cache.
 
drupal_static_reset('user_access');
 
drupal_static_reset('user_role_permissions');
?>
主要用于角色编辑或者删除等操作完成以后更新权限。

自定义表单调用autocomplete

<?php
<?
function
module_name_form() {
 
$form = array();
 
 
$form['city'] = array(
   
'#title' => t('City'),
   
'#type' => 'textfield',
   
'#autocomplete_path' => 'example/autocomplete',//--调用的路径
  
);
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => 'Save',
  );
 
  return
$form;
}

//--定义路径
function module_name_menu() { 
 
$items['example/autocomplete'] = array(
   
'page callback' => '_module_name_autocomplete', //--调用数据
   
'access arguments' => array('access example autocomplete'),
   
'type' => MENU_CALLBACK
 
);
  return
$items;
}

//--从数据库读取返回数据
function _module_name_autocomplete($string) {
 
$matches = array();
 
 
// Some fantasy DB table which holds cities
 
$query = db_select('cities', 'c');
 
// Select rows that match the string
 
$return = $query
   
->fields('c', array('city'))
    ->
condition('c.city', '%' . db_like($string) . '%', 'LIKE')
    ->
range(0, 10)
    ->
execute();
 
 
// add matches to $matches 
 
foreach ($return as $row) {
   
$matches[$row->city] = check_plain($row->city);
  }
 
 
// return for JS
 
drupal_json_output($matches); //--json格式返回
}
?>
http://timonweb.com/how-create-ajax-autocomplete-textfield-drupal-7