Yii Framework Forum: Templated views - Yii Framework Forum

Jump to content

  • (3 Pages)
  • +
  • 1
  • 2
  • 3
  • You cannot start a new topic
  • You cannot reply to this topic

Templated views Rate Topic: -----

#21 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 14 October 2008 - 12:08 PM

1 & 2 Sounds good to me, item 3 is what would normally occur in 90% of the cases, except for the example I have shown. The unassigned attribute "body" (CHtml::link) is a special case in which I allow the helper to capture the output of its body as a passed in value. So if you want to use an image for a link you could do something like
<helper:CHtml:link ...><helper:CHtml:image .../></helper:CHtml:link>

Currently only CHtml::link() has a "body" attribute which is applicable, but it may be useful for future "helpers"

nz
0

#22 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 14 October 2008 - 12:12 PM

I think the parser should not handle special cases (e.g. CHtml) inside. Make it as general as possible. If we want something like: <helper:CHtml:link ...><helper:CHtml:image .../></helper:CHtml:link>, we should create image widget and use <com:> tag to accomplish the goal.
0

#23 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 14 October 2008 - 12:19 PM

Sounds good to me, it was getting a bit hairy trying to do that  ;D

nz
0

#24 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 14 October 2008 - 04:35 PM

Ok input code

<h2>
Welcome, <?php echo Yii::app()->user->username; ?>!
</h2>
<p>
This is the homepage of <em><?php echo Yii::app()->name; ?></em>. You may modify the following files to customize the conent of this page:
</p>
<dl>
<dt><?php echo Yii::app()->controllerPath . DIRECTORY_SEPARATOR . 'SiteController.php'; ?></dt>
<dd>This file contains the <tt>SiteController</tt> class which is
the default application controller. Its default <tt>index</tt> action
renders the content of the following two files.
</dd>
<dt><?php echo __FILE__; ?></dt>
<dd>This is the view file that contains the body content of this page.</dd>
<dt><?php echo Yii::app()->layoutPath . DIRECTORY_SEPARATOR . 'main.php'; ?></dt>
<dd>This is the layout file that contains common presentation (such as header, footer) shared by all view files.</dd>
</dl>


<com:CCaptcha ButtonType="link" ShowRefreshButton="{true}">
<br />
</com:CCaptcha>
<com:CMultiFileUpload Name="mup"/>


Output code

<h2>
Welcome, <?php /** Line:2-2*/  echo Yii::app()->user->username; ?>!
</h2>
<p>
This is the homepage of <em><?php /** Line:5-5*/  echo Yii::app()->name; ?></em>. You may modify the following files to customize the conent of this page:
</p>
<dl>
<dt><?php /** Line:8-8*/  echo Yii::app()->controllerPath . DIRECTORY_SEPARATOR . 'SiteController.php'; ?></dt>
<dd>This file contains the <tt>SiteController</tt> class which is
the default application controller. Its default <tt>index</tt> action
renders the content of the following two files.
</dd>
<dt><?php /** Line:13-13*/  echo __FILE__; ?></dt>
<dd>This is the view file that contains the body content of this page.</dd>
<dt><?php /** Line:15-15*/  echo Yii::app()->layoutPath . DIRECTORY_SEPARATOR . 'main.php'; ?></dt>
<dd>This is the layout file that contains common presentation (such as header, footer) shared by all view files.</dd>
</dl>


<?php /** Line:20-20*/ $this->beginWidget('CCaptcha',array( "buttonType"=>'link',"showRefreshButton"=>true,)); ?>
<br />
<?php /** Line:22-22*/  $this->endWidget('CCaptcha'); ?>
<?php /** Line:23-23*/ $this->beginWidget('CMultiFileUpload',array( "name"=>'mup',));  $this->endWidget('CMultiFileUpload'); ?>


(For widgets the first letter of the attribute name is lower cased automatically)

Thoughts ?
nz
0

#25 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 14 October 2008 - 04:43 PM

The widget code looks good to me. Maybe <com:Widget /> can be translated to <?php $this->widget('Widget'); ?> instead of (beginWidget, endWidget) pair?

Also, maybe support translation from <%= expr; %> to <?php echo expr; ?> ?

Really nice work! Can I take a look at how your code about renderFile() method? Do not include the parsing part.
0

#26 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 14 October 2008 - 05:31 PM

The <%= %> are already there but my sample did not include them, Ill add in the widget thing...

here is the renderFile methods, is the implementation about right ?

  /**
  * Called by the context to render the file,
  * calls the getViewFileInternal to interrupt the working file
  */
  public function renderFile($context,$file,$data,$return) {
    // Call the context to render the template file
return $context->renderInternal($this->getViewFileInternal($file),$data,$return);
  }

/**
* Looks for the view file in the work folder.
* it is assumed the $viewFile points to a valid file.
*
* @param $viewFile the template file
* @param $chmod The folder attributes to set if folders need to be created
* @param $force True if you want to force the recompile of the view
*
* @return returns a file path to the "working" file.
*/
public function getViewFileInternal($viewFile, $chmod=755, $force=false)
{
    // Convert the view file to the work dir folder
    $newFilePath = $this->_workPath . substr($viewFile,$this->_basePathLen);

    if (!is_file($newFilePath) ||
        constant('YII_DEBUG')===true ||
        $force) {

      // Check to see if directory exists
      preg_match('`^(.+)[/\\]([a-zA-Z0-9]+.[a-z]+)$`i', $newFilePath, $matches);

      $directory = $matches[1];
      if (!is_dir($directory)){
        if (!mkdir($directory, $chmod, 1)){
        return FALSE;
        }
      }

      // If in debug mode check the dates on the file
      if (constant('YII_DEBUG')===true && is_file($newFilePath)) {
        if (filemtime($viewFile)>filemtime($newFilePath)) {
          // Call the parser to generate the file
          $this->generateFile($viewFile,$newFilePath);
        }
      }
      else {
        // Call the parser to generate the file
        $this->generateFile($viewFile,$newFilePath);
      }
    }

    // Return the working file path
return $newFilePath;
}

0

#27 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 14 October 2008 - 08:01 PM

Yeah, that's what I expected, but a slight difference.

I was thinking that the generated files should be put under the runtime directory since it is already configured to be writable. The directory structure under the runtime should look like the following

/protected/runtime/
views/
23ac234ef/
view1.php
view2.php
423aef3ec/
view1.php
view3.php       

where '23ac234ef' and '423aef3ec' are the CRC encoding of the directory part of the corresponding view files. For example, if we are rendering 'view1' of SiteController, then the CRC path would be generated using:

sprintf('%x',crc32(dirname($sourceViewFilePath)))



I also realized that using this parsing approach, we changed the value of __FILE__. So it needs to be careful. Maybe a substitution of __FILE__, too?
0

#28 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 14 October 2008 - 09:34 PM

Hi

Okay I modified the code to default the work path to the runtime/views folder

public function getViewFileInternal($viewFile, $chmod=755, $force=false)
{
    // Convert the view file to the work dir folder
    $pathName = dirname($viewFile);
    $fileName = basename($viewFile);
   
    $newFilePath = $this->_workPath . DIRECTORY_SEPARATOR .sprintf('%x', crc32($pathName)) .
                  DIRECTORY_SEPARATOR . $fileName;

    if (!is_file($newFilePath) ||



I also replaced the __FILE__ constant as I rendered the php code on the template, and I added a Visibility attribute to components or helpers
so this code
<com:CMultiFileUpload Name="mupHidden" Visibility="{false}"/>
renders this
<?php /** Line:25-25*/if (false===true)  $this->widget('CMultiFileUpload',array( "name"=>'mupHidden',));  ?>
0

#29 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 14 October 2008 - 09:48 PM

That's great, although I don't think the visibility property is quite necessary.

Congratulations for the nice work!

0

#30 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 15 October 2008 - 08:08 AM

thanks for the guidance, looking forward to the 1.0 beta release

NZ
0

#31 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 15 October 2008 - 10:36 AM

Hi

I was thinking of adding a couple of more tags like <view: and <cache: and have them call the beginContent/endContent and beginCache/endCache but after looking at the CBaseController I see those method calls simply pass the calls to the beginWidget / endWidget methods. So (if I added those two extra tags) should the generated code invoke the widgets directly or should I still call methods in the CBaseController ?

Thanks
NZ
0

#32 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 15 October 2008 - 10:42 AM

yeah, adding some new tags seems to be a great idea.
I would suggest using <yii:cache>, <yii:content> and <yii:clip>. You should call the corresponding CBaseController methods instead of widget methods. Also, pay attention to the <yii:cache> tag as its usage involves an "if" statement. It would be great the parser can automatically insert the if statement.
0

#33 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 15 October 2008 - 10:43 AM

A quick question: how do you handle quote escaping? For example, if a property value contains both single and double quotes, will you parser handle this?
0

#34 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 15 October 2008 - 10:52 AM

Every attribute is encoded like this

  protected static function encodeParamaterValue($value) {
    $value = htmlspecialchars_decode($value)
    if (substr($value,0,1)=="{" && substr($value,-1)=="}") {
      return substr($value,1,-1);
    }
   
    $escaped = preg_replace('{([\'\\\\])}', '\\\\$1', $value);
    $literal = '\'' . $escaped . '\'';
    return $literal;
  }

I'll add in those tags also.

NZ

Added $value = htmlspecialchars_decode($value) to function
0

#35 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 15 October 2008 - 11:00 AM

What I mean is if I enter the following:

<com:MyWidget name='It's my name' />

Will your parser handle this situation?
0

#36 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 15 October 2008 - 11:10 AM

The parser only allows double quoted attribute values so that would not be a valid tag

This would be valid
<com:MyWidget name="It's my name" />
and would be evaluated as array("name"=>'It\'s my name')

This would be illegal syntax
<com:MyWidget name="It"s my name" />

This would be valid
<com:MyWidget name="It&quot;s my name :\" />
and evaluated as array("name"=>'It"s my name :\\')

NZ
0

#37 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 15 October 2008 - 03:51 PM

With latest modifications :
Input

<h2>
Welcome, <%= Yii::app()->user->name; %>!
</h2>
<p>
This is the homepage of <em><%= Yii::app()->name; %></em>. You may modify the following files to customize the conent of this page:
</p>
<yii:cache id="MainCache">
  Cached data
</yii:cache>
<dl>
<dt><%= Yii::app()->controllerPath . DIRECTORY_SEPARATOR . 'SiteController.php'; %></dt>
<dd>This file contains the <tt>SiteController</tt> class which is
the default application controller. Its default <tt>index</tt> action
renders the content of the following two files.
</dd>
<dt><%= __FILE__ %></dt>
<dd>This is the view file that contains the body content of this page.</dd>
<dt><%= Yii::app()->layoutPath . DIRECTORY_SEPARATOR . 'main.php'; %></dt>
<dd>This is the layout file that contains common presentation (such as header, footer) shared by all view files.</dd>
</dl>
outside of clip
<yii:clip id="myClip" Visibility="{true}">
  <yii:view name="/testview">
    inside
    </yii:view>
  </yii:clip>
out of clip

<com:system.web.widgets.CCaptcha
      ButtonType="link"
      ShowRefreshButton="{true}">
<br />
</com:system.web.widgets.CCaptcha>
<com:CMultiFileUpload Name="mup" Visibility="{false}"/>
<%= $this->clips['myClip'] %>

output

<?php /* Source template file C:tempxamppxampplitehtdocstestdriveprotectedviewssiteindex.php */ ?><h2>
Welcome, <?php /** Line:2-2*/ echo  Yii::app()->user->name; ;  ?>!
</h2>
<p>
This is the homepage of <em><?php /** Line:5-5*/ echo  Yii::app()->name; ;  ?></em>. You may modify the following files to customize the conent of this page:
</p>
<?php /** Line:7-7*/ if ($this->beginCache('MainCache',array()) ) {  ?>
  Cached data
<?php /** Line:9-9*/ $this->endCache(); }    ?>
<dl>
<dt><?php /** Line:11-11*/ echo  Yii::app()->controllerPath . DIRECTORY_SEPARATOR . 'SiteController.php'; ;  ?></dt>
<dd>This file contains the <tt>SiteController</tt> class which is
the default application controller. Its default <tt>index</tt> action
renders the content of the following two files.
</dd>
<dt><?php /** Line:16-16*/ echo  'C:\temp\xampp\xampplite\htdocs\testdrive\protected\views\site\index.php' ;  ?></dt>
<dd>This is the view file that contains the body content of this page.</dd>
<dt><?php /** Line:18-18*/ echo  Yii::app()->layoutPath . DIRECTORY_SEPARATOR . 'main.php'; ;  ?></dt>
<dd>This is the layout file that contains common presentation (such as header, footer) shared by all view files.</dd>
</dl>
outside of clip
<?php /** Line:22-22*/if (true===true) {  $this->beginClip('myClip',array()) ;    ?>
  <?php /** Line:23-23*/ $this->beginContent('/testview',array()) ;    ?>
    inside
    <?php /** Line:25-25*/ $this->endContent();    ?>
  <?php /** Line:26-26*/ $this->endClip();  }  ?>
out of clip

<?php /** Line:29-31*/ $this->beginWidget('system.web.widgets.CCaptcha',array( "buttonType"=>'link',"showRefreshButton"=>true,));    ?>
<br />
<?php /** Line:33-33*/  $this->endWidget('system.web.widgets.CCaptcha');    ?>
<?php /** Line:34-34*/if (false===true) {  $this->beginWidget('CMultiFileUpload',array( "name"=>'mup',));  $this->endWidget('CMultiFileUpload');  }  ?>
<?php /** Line:35-35*/ echo  $this->clips['myClip'] ;  ?>


The only "bad" issue is (as you pointed out before) if the compiled file has a bad attribute set it the error could be misleading as to which physical view file is at fault.

NZ
0

#38 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 15 October 2008 - 04:10 PM

This now looks nearly perfect! Is <helper:> still in? How to use that?

One minor suggestion: remove Visibility or rename it to be Visible (easier to input).

Regarding error reporting, I think your solution is fine as you already inserted line numbers and file names in the compiled files. That's very good.


0

#39 User is offline   notzippy 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 201
  • Joined: 06-October 08

Posted 15 October 2008 - 07:29 PM

Yup still there

  <helper:CHtml:link body="Welcome"
      Url="{array('login')}"
      HtmlOptions="{array('confirm'=>'Are You Sure')}" />,
  <%= Yii::app()->user->name; %>!

Outputs

  <?php /** Line:2-3*/ echo CHtml::link( 'Welcome',array('login'),array('confirm'=>'Are You Sure'));  ?>,
<?php /** Line:4-4*/ echo  Yii::app()->user->name; ;  ?>!



I have changed the Visibility attribute to be called Visible

Is there a reason why we need to obfuscate the folders we are compiling the templates into (by using the crc32 function) ?

Thanks
NZ
0

#40 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,906
  • Joined: 04-October 08
  • Location:DC, USA

Posted 15 October 2008 - 08:16 PM

Without obfuscating the folder, you would end up with deeply nested directories. And if on windows, this would be very tricky if you attempt to keep the original directory structures.

One more suggestion, maybe we should use name={expr} instead of name="{expr}" ? The quotes are just extra typing that is not really necessary.
0

Share this topic:


  • (3 Pages)
  • +
  • 1
  • 2
  • 3
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users