CakePHP网站手机版

时间:2010-10-08 14:01:41

标签: cakephp subdomain mobile-website

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

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

8 个答案:

答案 0 :(得分:31)

我在app_controller.php文件中使用了对beforeFilter()的快速添加来完成此操作。

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

这使用CakePHP RequestHandler来感知它是否是访问我网站的移动设备。它设置属性和视图变量,以允许视图根据新布局调整自身的操作。同时关闭autoRender因为我们会在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);
        }
     }

答案 1 :(得分:5)

丹的回答对我有用。但是,我使用了file_exists而不是File构造函数,并添加了使用移动布局的功能。前过滤器是相同的,但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);
    }
 }

答案 2 :(得分:5)

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

简单地说:

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

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

答案 3 :(得分:4)

我为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中已弃用的词汇和常量。

答案 4 :(得分:2)

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

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

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

答案 5 :(得分:2)

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

此解决方案基于@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库...然后您可以定位平板电脑,甚至特定设计操作系统版本......哦,有趣! ^ _ ^

答案 6 :(得分: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-&gt; view和$ this-&gt;布局然后修改它们,而不是使用$ this-&gt; action和$ this-&gt; render(视图,布局),因为您的视图不会总是匹配您的操作(相同的视图,多个操作,例如,使用$ this-&gt;动作中断),此解决方案可以防止需要担心何时强制使用$ this-&gt; render()并允许它自然发生。

答案 7 :(得分:0)

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

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