【问题标题】:CakePHP website mobile versionCakePHP网站手机版
【发布时间】:2011-04-22 22:08:09
【问题描述】:

我已经使用 CakePHP 框架开发了一个完整的网站,我们想为移动设备(主要是 iPhone/iPad)制作一个非常轻量级的网站版本。

有没有办法将现有网站与一个新的子域(例如 mobile.mywebsite.com)一起呈现特定视图?我想避免复制和简化当前的以匹配新的要求。我不想在每次需要更改控制器操作时都“重新开发”一个新的 CakePHP 网站并进行两次更改。

【问题讨论】:

    标签: cakephp subdomain mobile-website


    【解决方案1】:

    我采用的解决方案是基于此处的一些答案进行的轻量级修改,适用于 CakePHP 2.5.5。处理全部在 beforeRender 中完成(请注意,beforeRender 仅在实际呈现页面的控制器操作上运行,因此与私有方法的 beforeFilter/afterFilter 相比,这节省了开销):

    $mobile = $this->request->is('mobile');
    $this->set('mobile',$mobile);
    //Check if an alternate mobile view and/or layout exists for this request.
    if($mobile){
        if(file_exists(APP.'View'.DS.$this->name.DS.'mobile'.DS.$this->view.'.ctp')){
            //Render this action on its mobile view.
            $this->view = 'mobile'.DS.$this->view;
        }
        if(file_exists(APP.'View'.DS.'Layouts'.DS.'mobile'.DS.$this->layout.'.ctp' )){
            //Render this action on its mobile layout.
            $this->layout = 'mobile'.DS.$this->layout;
        }
    }
    

    $mobile 变量可以在任何视图上使用,如果您需要进行一些小的调整,否则您可以选择将任何视图替换为 View/{controller}/mobile/same_file_name.ctp 或布局替换为 View/Layouts/mobile/same_file_name .ctp 具有完全独立的页面结构。

    注意 this 使用 $this->view 和 $this->layout 然后修改它们,而不是使用 $this->action 和 $this->render(view,layout),因为你的视图不会总是匹配您的操作(同一视图,多个操作,例如,使用 $this->action 中断),并且此解决方案无需担心何时强制执行 $this->render(),并允许它自然发生。

    【讨论】:

    • 这个对我来说是最容易实现的——5/20/15
    【解决方案2】:

    我通过快速添加到我的 app_controller.php 文件中的 beforeFilter() 来完成此操作。

    function beforeFilter() {
         if ($this->RequestHandler->isMobile()) {
            $this->is_mobile = true;
            $this->set('is_mobile', true );
            $this->autoRender = false;
         }
    }
    

    这使用 CakePHP RequestHandler 来检测它是否是移动设备访问我的网站。它设置一个属性和视图变量,以允许视图根据新布局调整自己的操作。同时关闭自动渲染,因为我们将在 afterFilter 中处理它。

    在 afterFilter() 中,它查找并使用移动视图文件(如果存在)。移动版本存储在控制器视图文件夹内的“移动”文件夹中,并且命名与普通非移动版本完全相同。 (即 add.ctp 变为 mobile/add.ctp)

        function afterFilter() {
            // if in mobile mode, check for a valid view and use it
            if (isset($this->is_mobile) && $this->is_mobile) {
                $view_file = new File( VIEWS . $this->name . DS . 'mobile/' . $this->action . '.ctp' );
                $this->render($this->action, 'mobile', ($view_file->exists()?'mobile/':'').$this->action);
            }
         }
    

    【讨论】:

    • 嗨,Dan,您认为是否可以将您的解决方案与子域一起使用,然后让用户通过链接运行整个网站(如果他愿意)?干杯,
    • 可能。我的解决方案自动感知正在浏览的设备。也许只需添加一个设置 cookie 的链接以覆盖浏览器感应。那么就不需要子域了。只是一个设置 cookie 的“查看完整站点”链接。
    • cookie 确实是个好主意。我会在这个周末考虑一下,但是您易于配置的解决方案很可能会得到我的投票。干杯,
    • 在 cake 2.x 中这将是 $this->RequestHandler->is('mobile')
    • 我想在这个解决方案中添加一件事,这里使用 $this->name 来获取控制器名称,当 URL 中的控制器名称中没有下划线时,这可以正常工作,在下划线的情况下这将导致错误,因此应该使用更好的 $this->params['controller'],因此 $view_file = new File( VIEWS . $this->params['controller'] . DS . 'mobile/' . $this->action . '.ctp' );应该用于安全。
    【解决方案3】:

    您可以在 CakePHP 2.x 中使用 Theme feature 进行移动布局。

    简单地做:

    if($this->RequestHandler->isMobile())
        $this->theme = 'mobile';
    

    我发现这更好,因为您可以轻松地在移动和桌面主题上共享查看文件。

    【讨论】:

    • 我也喜欢主题方法。不过有一个变化,最好使用CakeRequest 而不是RequestHandlerComponent。 if ($this->request->is('mobile')) {...
    【解决方案4】:

    CakePHP v2.2.1 解决方案(+ Cookies 保持移动/桌面/其他布局)

    此解决方案基于@Dan Berlyoung@deewilcox@Chris K 的回答。

    这些答案的一部分在 CakePHP 2.2.1 中(对我来说)不起作用。

    我还扩展了该解决方案,以支持从前端“强制”移动/桌面/其他布局 - 对于调试和不想被“移动”主题布局卡住的用户很有用。

    /app/Controller/AppController.php

    class AppController extends Controller {
    
      public $components = array('Cookie');
      public $is_mobile = false;
      public $layouts = array('desktop', 'mobile');
    
      // executed before every action in the controller
      function beforeFilter() 
      {
        // Using "rijndael" encryption because the default "cipher" type of encryption fails to decrypt when PHP has the Suhosin patch installed. 
        // See: http://cakephp.lighthouseapp.com/projects/42648/tickets/471-securitycipher-function-cannot-decrypt
        $this->Cookie->type('rijndael');
    
        // When using "rijndael" encryption the "key" value must be longer than 32 bytes.
        $this->Cookie->key = 'qSI242342432qs*&sXOw!adre@34SasdadAWQEAv!@*(XSL#$%)asGb$@11~_+!@#HKis~#^'; // When using rijndael encryption this value must be longer than 32 bytes.
    
        // Flag whether the layout is being "forced" i.e overwritten/controlled by the user (true or false)
        $forceLayout = $this->Cookie->read('Options.forceLayout');
    
        // Identify the layout the user wishes to "force" (mobile or desktop)
        $forcedLayout = $this->Cookie->read('Options.forcedLayout');
    
        // Check URL paramaters for ?forcedLayout=desktop or ?forcedLayout=mobile and persist this decision in a COOKIE
        if( isset($this->params->query['forcedLayout']) && in_array($this->params->query['forcedLayout'], $this->layouts) )
        {
            $forceLayout = true;
            $forcedLayout = $this->params->query['forcedLayout'];
            $this->Cookie->write('Options.forceLayout', $forceLayout);
            $this->Cookie->write('Options.forcedLayout', $forcedLayout);
        }
    
        // We use CakePHP's built in "mobile" User-Agent detection (a pretty basic list of UA's see: /lib/Cake/Network/CakeRequest.php)
        // Note: For more robust detection consider using "Mobile Detect" (https://github.com/serbanghita/Mobile-Detect) or WURL (http://wurfl.sourceforge.net/)
        if( ( $forceLayout && $forcedLayout == 'mobile' ) || ( !$forceLayout && $this->request->is('mobile') ) )  {
            $this->is_mobile = true;
            $this->autoRender = false; // take care of rendering in the afterFilter()
        }
    
        $this->set('is_mobile', $this->is_mobile);
      }
    
      // executed after all controller logic, including the view render.
      function afterFilter() {
    
        // if in mobile mode, check for a vaild layout and/or view and use it
        if( $this->is_mobile ) {
            $has_mobile_view_file = file_exists( ROOT . DS . APP_DIR . DS . 'View' . DS . $this->name . DS . 'mobile' . DS . $this->action . '.ctp' );
            $has_mobile_layout_file = file_exists( ROOT . DS . APP_DIR . DS . 'View' . DS . 'Layouts' . DS . 'mobile' . DS . $this->layout . '.ctp' );
    
            $view_file = ( $has_mobile_view_file ? 'mobile' . DS : '' ) . $this->action;
            $layout_file = ( $has_mobile_layout_file ? 'mobile' . DS : '' ) . $this->layout;
    
            $this->render( $view_file, $layout_file );
        }
      }
    }
    

    /app/View/Elements/default_footer.ctp

    <ul>
        <?php
            $paramsQuery = $this->params->query;
            if(!is_array($paramsQuery))
            {
                $paramsQuery = array();
            }
            $paramsQuery['url'] = ( isset($paramsQuery['url']) ) ? $paramsQuery['url'] : '';
            $url = $paramsQuery['url'];
            unset($paramsQuery['url']);
            $params = $paramsQuery;
    
            $mobile_url = '/' . $url . '?' . http_build_query( array_merge( $params, array( 'forcedLayout' => 'mobile' ) ) );
            $desktop_url = '/' . $url . '?' . http_build_query( array_merge( $params, array( 'forcedLayout' => 'desktop' ) ) );
        ?>
    
        <?php if($is_mobile): ?>
            <li><?= $this->Html->link('Desktop Site', $desktop_url, array('target' => '', 'class' => '')) ?></li>
        <?php else: ?>
            <li><?= $this->Html->link('Mobile Site', $mobile_url, array('target' => '', 'class' => '')) ?></li>
        <?php endif; ?>
    </ul>
    

    /app/View/Layouts/default.ctp

    <h1>Desktop Site Layout</h1>
    <?= $this->fetch('content') ?>
    

    /app/View/Layouts/mobile/default.ctp

    <h1>Mobile Site Layout</h1>
    <?= $this->fetch('content') ?>
    

    /app/View/Pages/home.ctp

    <h2>Home - on Desktop</h2>
    <?= $this->element('default_footer') ?>
    

    /app/View/Pages/mobile/home.ctp

    <h2>Home - on Mobile</h2>
    <?= $this->element('default_footer') ?>
    

    用法

    使用default_footer 链接更改布局 - 或这些直接网址
    http://example.com/pages/home?forcedLayout=desktop
    http://example.com/pages/home?forcedLayout=mobile

    会话 COOKIE 会保留您选择的选项...例如尝试设置为“移动”,然后访问没有forcedLayout= 参数的网址。
    http://example.com/pages/home

    default_footer 链接保留现有参数(“片段”#gohere 除外)
    http://example.com/pages/home/a/b/c:d?this=that&foo=bar#gohere

    桌面网址为:
    http://example.com/pages/home/a/b/c:d?this=that&foo=bar&forcedLayout=desktop

    要获得更强大的设备用户代理检测,请考虑使用Mobile Detect PHP 库...然后您可以针对平板电脑,甚至特定的设计操作系统版本...哦,真有趣! ^_^

    【讨论】:

    • 谢谢克里斯的回答。大多数客户都在寻找 m.example.com 样式的子域。您对将您的解决方案与子域使用集成有什么建议吗?
    【解决方案5】:

    我为一个 CakePHP 2.1 应用程序修改了这项技术。这是我的beforeFilter()

    public function beforeFilter() {
    
    if ($this->request->isMobile()){
        $this->is_mobile = true;
            $this->set('is_mobile', true );
            $this->autoRender = false;
    }
    
    }
    

    这是我的afterFilter()

    function afterFilter() {
        // if in mobile mode, check for a valid view and use it
        if (isset($this->is_mobile) && $this->is_mobile) {
            $view_file = file_exists( 'Views' . $this->name . DS . 'mobile/' . $this->action . '.ctp' );
            $layout_file = file_exists( 'Layouts' . 'mobile/' . $this->layout . '.ctp' );
            if($view_file || $layout_file){
                $this->render($this->action, ($layout_file?'mobile/':'').$this->layout, ($view_file?'mobile/':'').$this->action);
            }
        }
     }
    

    这有助于说明 CakePHP 2 中已弃用的措辞和常量。

    【讨论】:

    • @bancer 的技术也适用于 CakePHP 2.1,如果您有一个可以处理您网站上所有页面的移动布局。
    【解决方案6】:

    简单的解决方案是使用相应的样式表创建一个新的“移动”布局并在 AppController 中打开它:

    public $components = array('RequestHandler');
    public function beforeRender() {
        parent::beforeRender();
        if ($this->RequestHandler->isMobile()) {
            $this->layout = 'mobile';
        }
    }
    

    如果您在控制器的方法中更改 $this-&gt;layout,请务必在 beforeRender() 中执行此操作。

    【讨论】:

      【解决方案7】:

      丹的回答对我有用。但是,我使用了 file_exists 而不是 File 构造函数,并添加了使用移动布局的功能。 before 过滤器是一样的,但是 afterFilter 看起来像这样:

      function afterFilter() {
          // if in mobile mode, check for a valid view and use it
          if (isset($this->is_mobile) && $this->is_mobile) {
              $view_file = file_exists( VIEWS . $this->name . DS . 'mobile/' . $this->action . '.ctp' );
              $layout_file = file_exists( LAYOUTS . 'mobile/' . $this->layout . '.ctp' );
      
              $this->render($this->action, ($layout_file?'mobile/':'').$this->layout, ($view_file?'mobile/':'').$this->action);
          }
       }
      

      【讨论】:

      • 在 Linux 服务器上的 CakePHP 1.3 中,您可能需要添加 strtolower($this->name),因为控制器名称的首字母大写,导致在 linux 服务器上找不到文件,因为它区分大小写.
      • 我们可以用if(!empty($this-&gt;_is_mobile)) 代替if(isset($this-&gt;is_mobile &amp;&amp; $this-&gt;is_mobile) 吗?
      【解决方案8】:

      是的,您可以重复使用您的所有域和控制器,看看Tera-WURLF

      更棒的是,移动版不需要子域。

      【讨论】:

      • 您好 mcabral,谢谢您的建议。我真的很喜欢子域的想法,因为人们可以根据需要切换到完整的站点。对此功能有任何想法吗?你用过吗?干杯,
      • @Nicolas 是的,我已经使用 Zend Framework 进行了尝试。这是一个很好的工具。
      猜你喜欢
      • 1970-01-01
      • 2014-08-14
      • 1970-01-01
      • 1970-01-01
      • 2022-01-10
      • 1970-01-01
      • 2011-12-29
      • 1970-01-01
      • 2011-01-05
      相关资源
      最近更新 更多