一个使用Zend framework 1.9写的简单博客程序 - ZF Dream

一个使用Zend framework 1.9写的简单博客程序

阿克 posted @ 2009年9月30日 22:33 in 海外教程 with tags Zend Framework php , 25705 阅读

我曾用Zend framework 1.8工作,现在用1.9。我做了一个简单的博客程序。我说的博客就是类似Wordpress的东西,可以发文章、加评论,现在还能从Picasa上抓相册。

我是一个Zend framework和MVC的新手,我知道里面有些错误,有一些是我图方便而产生的。同时我也是单元测试的新手,所以里面没有写什么测试数据代码。抱歉。我知道这个程序的缺点,数据库也不太好。但我还是把它发布了,希望能帮助一些人理解Zend_ACL、Zend_Auth、Zend_Form等。我在里面还加了一个图片集功能,用到了Zend_Gdata接口,可以从Picasa读取照片和相册,未来我考虑通过管理界面来增加照片,以及在管理界面做一些特色,如果我有时间的话。

我看过各式各样的博客程序,有些已经过时了,但有些仍然生机勃勃。我在用Zend Toolkit,因为相比图形界面,我更喜欢使用命令行。我提供了完整的Zend framework 1.9博客程序例的下载链接,大家可以在文末的链接处下载。

开发环境:

1)LAMP或WAMP或MAMP

2)配置好的Zendframework 1.9

3)我用eclipse、netbeans或zend studio i5编辑,如果你用zend studio i5,就不用看下面命令行的部分,许多东西已经在里面有了。

我希望你已经配置好了ZF,现在让我们用命令行来创建项目、控制器和动作。符号$代表命令行。(ZF Dream翻译) 

$ zf create project blog

这就创建了ZF 1.8+的目录结构。切换到项目所在目录,如cd <项目名称>。输入命令: 

$ zf show profile

ProjectDirectory

    ProjectProfileFile

    ApplicationDirectory

        ConfigsDirectory

            ApplicationConfigFile

        ControllersDirectory

            ControllerFile

                ActionMethod

            ControllerFile

        ModelsDirectory

        ViewsDirectory

            ViewScriptsDirectory

                ViewControllerScriptsDirectory

                    ViewScriptFile

                ViewControllerScriptsDirectory

                    ViewScriptFile

            ViewHelpersDirectory

        BootstrapFile

    LibraryDirectory

    PublicDirectory

        PublicIndexFile

        HtaccessFile

    TestsDirectory

        TestPHPUnitConfigFile

        TestApplicationDirectory

            TestApplicationBootstrapFile

        TestLibraryDirectory

            TestLibraryBootstrapFile

上面就是显示的目录结构。下面我们来创建控制器和动作: 

$ zf create controller posts

 

Creating a controller at //var/www/blog/application/controllers/PostsController.php

Creating an index action method in controller posts

Creating a view script for the index action method at //var/www/blog/application/views/scripts/posts/index.phtml

Creating a controller test file at //var/www/blog/tests/application/controllers/PostsControllerTest.php

Updating project profile '//var/www/blog/.zfproject.xml' 

 

$ zf create action view posts

 

Creating an action named view inside controller at //var/www/blog/application/controllers/PostsController.php

Updating project profile '//var/www/blog/.zfproject.xml'

Creating a view script for the view action method at //var/www/blog/application/views/scripts/posts/view.phtml

Updating project profile '//var/www/blog/.zfproject.xml' 

 

$ zf create action edit posts

 

Creating an action named edit inside controller at //var/www/blog/application/controllers/PostsController.php

Updating project profile '//var/www/blog/.zfproject.xml'

Creating a view script for the edit action method at //var/www/blog/application/views/scripts/posts/edit.phtml

Updating project profile '//var/www/blog/.zfproject.xml' 

 

$ zf create action add posts

 

Creating an action named add inside controller at //var/www/blog/application/controllers/PostsController.php

Updating project profile '//var/www/blog/.zfproject.xml'

Creating a view script for the add action method at //var/www/blog/application/views/scripts/posts/add.phtml

Updating project profile '//var/www/blog/.zfproject.xml' 

 

$ zf create action login index

 

Creating an action named login inside controller at //var/www/blog/application/controllers/IndexController.php

Updating project profile '//var/www/blog/.zfproject.xml'

Creating a view script for the login action method at //var/www/blog/application/views/scripts/index/login.phtml

Updating project profile '//var/www/blog/.zfproject.xml' 

 

$ zf create action logout index

 

Creating an action named logout inside controller at //var/www/blog/application/controllers/IndexController.php

Updating project profile '//var/www/blog/.zfproject.xml'

Creating a view script for the logout action method at //var/www/blog/application/views/scripts/index/logout.phtml

Updating project profile '//var/www/blog/.zfproject.xml'

 

建立数据库,并添加用户表、评论表和文章表。 

CREATE TABLE IF NOT EXISTS `comments` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `Post_id` int(11) NOT NULL,

  `Description` varchar(300) NOT NULL,

  `Name` varchar(200) NOT NULL,

  `Email` varchar(250) NOT NULL,

  `Webpage` varchar(200) NOT NULL,

  `Postedon` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

  

CREATE TABLE IF NOT EXISTS `posts` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `Title` varchar(250) NOT NULL,

  `Description` text NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

  

CREATE TABLE IF NOT EXISTS `users` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `Username` varchar(200) NOT NULL,

  `Password` varchar(250) NOT NULL,

  `Role` varchar(10) NOT NULL,

  `Name` varchar(200) NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

 

在Bootstrap.php增加如下代码,这样你就可以象__autoload()一样自动加载类。(ZF Dream翻译) 

protected function _initAutoload()

    {

        $moduleLoader = new Zend_Application_Module_Autoloader(array(

            'namespace' => '',

            'basePath' => APPLICATION_PATH));

        return $moduleLoader;

    }

 

在application/configs/application.ini中加上布局和数据库连接信息。完整代码如下: 

[production]

phpSettings.display_startup_errors = 0

phpSettings.display_errors = 0

includePaths.library = APPLICATION_PATH "/../library"

bootstrap.path = APPLICATION_PATH "/Bootstrap.php"

bootstrap.class = "Bootstrap"

resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"

resources.view[] = 

resources.db.adapter = PDO_MYSQL

resources.db.params.host = localhost

resources.db.params.username = root

resources.db.params.password = 

resources.db.params.dbname = hari_zendblog

[staging : production]

[testing : production]

phpSettings.display_startup_errors = 1

phpSettings.display_errors = 1

[development : production]

phpSettings.display_startup_errors = 1

phpSettings.display_errors = 1

 

 

编写布局文件:

application/layouts/scripts/layout.phtml 

<?php 

echo $this->doctype() ?>

<html xmlns="http://www.w3.org/1999/xhtml"> 

<head>  

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 

  <title>Bloggers turns to developers</title>

  <?php echo $this->headLink()->appendStylesheet('/quickstart/public/css/global.css') ?>

  <?php echo $this->headLink()->prependStylesheet($this->baseUrl().'/css/main.css'); ?>

</head> 

<body>

<div id="header" style="background-color: #EEEEEE; height: 30px;">

    <div id="header-logo" style="float: left">

        <b><a href="<?php echo $this->baseurl(); ?>">ZF Quickstart Application</a></b>    

        <a href="<?php 

        if( Zend_Auth::getInstance()->hasIdentity() ) { 

            $str = "/index/logout";

            $log = "Logout";  

           } else { 

               $str = "/index/login";

               $log = "Login";  

           } 

           echo $this->baseurl().$str; ?>"><?php echo $log; ?></a>

    </div>

</div>

<div><?php echo $this->layout()->content ?></div>

</body>

</html>

 

 

你可以通过phpmyadmin或其他方式插入一些一些测试数据,这样前端就能显示出一些文章来。把下面的代码加到IndexController的indexAction中去:(ZF Dream翻译

public function indexAction()

    {

        // action body

        $posts = new Model_DbTable_Posts();

        $result = $posts->getPosts();

	/* 下面是分页代码 */

        $page = $this->_getParam('page',1);

        $paginator = Zend_Paginator::factory($result);

        $paginator->setItemCountPerPage(2);

	/* 每页的文章数。我写成2这样你可以容易得看到分页效果. */

        $paginator->setCurrentPageNumber($page);

        $this->view->paginator = $paginator;

    }

  

以下是完整的IndexController.php代码: 

<?php

class IndexController extends Zend_Controller_Action

{

    public function init()

    {

        /* Initialize action controller here */

    }

    public function indexAction()

    {

        // action body

        $posts = new Model_DbTable_Posts();

        $result = $posts->getPosts();

        $page = $this->_getParam('page',1);

        $paginator = Zend_Paginator::factory($result);

        $paginator->setItemCountPerPage(2);

        $paginator->setCurrentPageNumber($page);

        $this->view->paginator = $paginator;

    }

    public function loginAction()

    {        

        $loginForm = new Form_Login();

        $redirect = $this->getRequest()->getParam('redirect', 'index/index');

        $loginForm->setAttrib('redirect', $redirect );

        $auth = Zend_Auth::getInstance();

        if(Zend_Auth::getInstance()->hasIdentity()) {

            $this->_redirect('/index/hello');

        } else if ($this->getRequest()->isPost()) {

            if ( $loginForm->isValid($this->getRequest()->getPost()) ) {

                $username = $this->getRequest()->getPost('username');

                $pwd = $this->getRequest()->getPost('pass');

                $authAdapter = new Model_AuthAdapter($username, $pwd);

                $result = $auth->authenticate($authAdapter);

                if(!$result->isValid()) {

                    switch ($result->getCode()) {

                       case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:

                            $this->view->error = 'user credentials not found';

                    }

                } else {

                    //Successfully logged in

                    $this->_redirect( $redirect );

                }

            }

        }

        $this->view->loginForm = $loginForm;

//        $this->_redirect($redirectUrl);

    }

    public function logoutAction()

    {

        $auth = Zend_Auth::getInstance();

        $auth->clearIdentity();

        $this->_redirect('/');

    }

}

 

 

现在你需要创建一个文章表的连接,以便我们来做插入和更改操作。这部分内容写到application/Models/DbTable/Posts.php里去。 

<?php

class Model_DbTable_Posts extends Zend_Db_Table_Abstract

{

    protected $_name = 'posts';

    public function getPosts()

    {

        $orderby = array('id DESC');

        $result = $this->fetchAll('1', $orderby );

        return $result->toArray();

    }

    public function getPost( $id )

    {

        $id = (int)$id;

        $row = $this->fetchRow('id = ' . $id);

        if (!$row) {

            throw new Exception("Count not find row $id");

        }

        return $row->toArray();

    }

    /*

     * Add new posts

     */

    public function savePost( $post )

    {

        $data = array( 'Title'=> $post['Title'],

                'Description'=> $post['Description']);

        $this->insert($data);

    }

    /*

     * Update old posts

     */

    public function updatePost( $post )

    {

        $data = array(

                'id'=> $post['id'],

                'Title'=> $post['Title'],

                'Description'=> $post['Description']);

        $where = 'id = '.$post['id'];

        $this->update($data , $where );

    }

}

  

创建application/Models/DbTable/Users.php: 

<?php

class Model_DbTable_Users extends Zend_Db_Table_Abstract

{

    protected $_name = 'users';

    public function findCredentials($username, $pwd)

    {

        $select = $this->select()->where('username = ?', $username)

            ->where('password = ?', $this->hashPassword($pwd));

        $row = $this->fetchRow($select);

        if($row) {

            return $row;

        }

        return false;

    }

    protected function hashPassword($pwd)

    {

        return md5($pwd);

    }

}

  

创建application/Models/DbTable/Comments.php: 

<?php

class Model_DbTable_Comments extends Zend_Db_Table_Abstract

{

    protected $_name = 'comments';

    public function getComments( $postid ) 

    {

        $result = $this->fetchAll( "post_id = '$postid'"  );

        return $result->toArray();

    }

    public function saveComment( $commentForm )

    {

        $data = array('post_id' => $commentForm['id'] ,

                'Description' => $commentForm['comment'],

                'Name' => $commentForm['name'],

                'Email' => $commentForm['email'],

                'Webpage' => $commentForm['webpage'] );

        $this->insert($data);

    }

}

 

现在我们来建立登录表单/application/forms/Login.php:(ZF Dream翻译) 

<?php

class Form_Login extends Zend_Form

{

    public function __construct()

    {

        parent::__construct($options);

        $this->setName('UserLogin');

        $username = new Zend_Form_Element_Text('username');

        $username->setLabel('User Name')

                ->setRequired(true)

                ->addFilter('StripTags')

                ->addFilter('StringTrim')

                ->addValidator('NotEmpty');

        $pass = new Zend_Form_Element_Password('pass');

        $pass->setLabel('Password')

                ->setRequired(true)

                ->addFilter('StripTags')

                ->addFilter('StringTrim')

                ->addValidator('NotEmpty');

        $submit = new Zend_Form_Element_Submit('submit');

        $redirect = new Zend_Form_Element_Hidden('redirect');

        $submit->setAttrib('id', 'submitbutton');

        $this->addElements( array ( $username, $pass, $submit));

    }

}

 

 

创建一个表单,用于发文章:

application/forms/posts.php 

<?php

class Form_Post extends Zend_Form

{

    public function __construct()

    {

        parent::__construct($options);

        $this->setName('Posts');

        $id = new Zend_Form_Element_Hidden('id');

        $title = new Zend_Form_Element_Text('Title');

        $title->setLabel('Title')

                ->setRequired(true)

                ->addFilter('StripTags')

                ->addFilter('StringTrim')

                ->addValidator('NotEmpty');

        $description = new Zend_Form_Element_Textarea('Description');

        $description->setLabel('Description')

                ->setRequired(true)

                ->setAttrib('rows',20)

                ->setAttrib('cols',50)

                ->addFilter('StripTags')

                ->addFilter('StringTrim')

                ->addValidator('NotEmpty');

        $submit = new Zend_Form_Element_Submit('submit');

        $submit->setAttrib('id', 'submitbutton');

        $this->addElements( array( $id, $title, $description, $submit ));

    }

}

 

创建一个表单,用于发评论:(ZF Dream翻译

application/forms/Comments.php 

<?php

class Form_Comments extends Zend_Form

{

    public function __construct()

    {

        $acl = new Model_Acl();

        $identity = Zend_Auth::getInstance()->getIdentity();

        /*

        * 检查是否允许发评论

        */

        if( Zend_Auth::getInstance()->hasIdentity()

        && $acl->isAllowed( $identity['role'] ,'comments','add') ) {

            parent::__construct($options);

            $this->setName('Comments');

            $id = new Zend_Form_Element_Hidden('id');

            $name = new Zend_Form_Element_Text('name');

            $name->setLabel('Your Name')

            ->setRequired(true)

            ->addFilter('StripTags')

            ->addFilter('StringTrim')

            ->addValidator('NotEmpty');

            $email = new Zend_Form_Element_Text('email');

            $email->setLabel('Email')

            ->setRequired(true)

            ->addFilter('StripTags')

            ->addFilter('StringTrim')

            ->addValidator('NotEmpty');

            $webpage = new Zend_Form_Element_Text('webpage');

            $webpage->setLabel('Webpage')

            ->setRequired(true)

            ->addFilter('StripTags')

            ->addFilter('StringTrim')

            ->addValidator('NotEmpty');

            $comment = new Zend_Form_Element_Textarea('comment');

            $comment->setLabel('Comments')

            ->setRequired(true)

            ->setAttrib('rows',7)

            ->setAttrib('cols',30)

            ->addFilter('StripTags')

            ->addFilter('StringTrim')

            ->addValidator('NotEmpty');

            $submit = new Zend_Form_Element_Submit('submit');

            $submit->setAttrib('id', 'submitbutton');

            $this->addElements( array ($id, $name, $email, $webpage, $comment, $submit));

        }

    }

}

  

创建application/Models/Acl.php 

<?php

class Model_Acl extends Zend_Acl 

{

    public function __construct() 

    {

        /*

         *  增加一个新角色"guest"

         *  Guest能浏览所有内容

         */

        $this->addRole(new Zend_Acl_Role('guest'));

        /* 

         * 增加user角色继承guest

         * Users能发评论

         */

        $this->addRole(new Zend_Acl_Role('user'), 'guest');

        /* 

         * 增加blogger角色继承ueser

         * Bloggers能发文章

         */

        $this->addRole(new Zend_Acl_Role('blogger'), 'user');

        /*

         * 增加admin角色继承blogger

         * 拥有所有的权利

         */

        $this->addRole(new Zend_Acl_Role('admin'), 'blogger');

        //增加posts资源

        $this->add(new Zend_Acl_Resource('posts'));

        //增加edit资源,继承posts

        //$this->add(new Zend_Acl_Resource('edit'), 'posts');

        //增加edit资源

        //$this->add(new Zend_Acl_Resource('add'), 'posts');

        //最后,我们让guest可以浏览所有的内容

        $this->allow('guest', 'posts', 'view');

        // Bloggers能发文章,修改文章

        $this->allow('blogger', 'posts', 'edit');

        $this->allow('blogger', 'posts', 'add');

    }

}

 

 创建application/Models/AuthAdapter.php 

<?php

class Model_AuthAdapter implements Zend_Auth_Adapter_Interface

{

    protected $username;

    protected $password;

    protected $user;

    public function __construct($username, $password) {

        $this->username = $username;

        $this->password = $password;

        $this->user = new Model_DbTable_Users();

    }

    public function authenticate()

    {

        $match = $this->user->findCredentials($this->username, $this->password);

        //var_dump($match);

        if(!$match) {

            $result = new Zend_Auth_Result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID, null);

        } else {

            $user = current($match);

            $result = new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $user);

        }

        return $result;

    }

}

  

创建application/views/helpers/BaseUrl.php 

<?php

class Zend_View_Helper_BaseUrl {

    function baseUrl() {

        $fc = Zend_Controller_Front::getInstance();

        return $fc->getBaseUrl();

    }

}

?>

 

 创建application/views/helpers/LinkTo.php 

<?php

class Zend_View_Helper_LinkTo

{

    protected static $baseUrl = null;

    public function linkTo($path)

    {

        if (self::$baseUrl === null) {

            $request = Zend_Controller_Front::getInstance()->getRequest();

            $root = '/' . trim($request->getBaseUrl(), '/');

            if ($root == '/') {

                $root = '';

            }

            self::$baseUrl = $root . '/';

        }

        return self::$baseUrl . ltrim($path, '/');

    }

}

 

 创建application/views/helpers/LoggedInUser.php 

<?php

class Zend_View_Helper_LoggedInUser

{

    protected $_view;

    function setView($view)

    {

    $this->_view = $view;

    }

    function loggedInUser()

    {

        $auth = Zend_Auth::getInstance();

        if($auth->hasIdentity())

        {

            $logoutUrl = $this->_view->linkTo('auth/logout');

            $user = $auth->getIdentity();

            $username = $this->_view->escape(ucfirst($user->username));

            $string = 'Logged in as ' . $username . ' | <a href="' .

            $logoutUrl . '">Log out</a>';

        } else {

            $loginUrl = $this->_view->linkTo('auth/identify');

            $string = '<a href="'. $loginUrl . '">Log in</a>';

        }

        return $string;

    }

}

  

用下面的内容来替换掉默认的application/views/scripts/index/index.phtml: 

<?php if (count($this->paginator)): ?>

<?php foreach ($this->paginator as $post ): ?>

<div class="post">

  <div class="title"><a href="<?php echo $this->baseUrl()."/posts/view/id/".$post['id']; ?>"><?php echo $this->escape($post['Title']); ?></a></div>

  <div class="description"><?php echo nl2br(substr( strip_tags( $post['Description']), 0 , 300)); ?></div>

  <div class="commentscount">Comments( count )</div>

</div>

<?php endforeach; ?>

<?php endif; ?>

<div class="pagination"><?php echo $this->paginationControl($this->paginator,

                                    'Sliding',

                                    '/partials/my_pagination_control.phtml'); ?></div>

  

用下面的内容来替换application/views/scripts/index/login.phtml 

<?php echo $this->errors; ?>

<?php echo $this->loginForm; ?>

 

 用下面的内容来替换application/views/scripts/index/logout.phtml 

<div>Successfully Logged out</div>

  

创建application/views/scripts/partials/my_pagination_control.phtml(你可以在不同的目录里放占位符,我忘记替换了。)(ZF Dream翻译) 

<?php if ($this->pageCount): ?>

<div class="paginationControl">

<!-- 上一页链接 -->

<?php if (isset($this->previous)): ?>

  <a href="<?php echo $this->url(array('page' => $this->previous)); ?>">

    < Previous

  </a> |

<?php else: ?>

  <span class="disabled">< Previous</span> |

<?php endif; ?>

<!-- 页码链接 -->

<?php foreach ($this->pagesInRange as $page): ?>

  <?php if ($page != $this->current): ?>

    <a href="<?php echo $this->url(array('page' => $page)); ?>">

        <?php echo $page; ?>

    </a> |

  <?php else: ?>

    <?php echo $page; ?> |

  <?php endif; ?>

<?php endforeach; ?>

<!-- 下一页链接 -->

<?php if (isset($this->next)): ?>

  <a href="<?php echo $this->url(array('page' => $this->next)); ?>">

    Next >

  </a>

<?php else: ?>

  <span class="disabled">Next ></span>

<?php endif; ?>

</div>

<?php endif; ?>

<?php

/*

 * Drop down paginaton example

 */

/*

?>

<?php if ($this->pageCount): ?>

<select id="paginationControl" size="1">

<?php foreach ($this->pagesInRange as $page): ?>

  <?php $selected = ($page == $this->current) ? ' selected="selected"' : ''; ?>

  <option value="<?php

        echo $this->url(array('page' => $page));?>"<?php echo $selected ?>>

    <?php echo $page; ?>

  </option>

<?php endforeach; ?>

</select>

<?php endif; ?>

<script type="text/javascript"

     src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js">

</script>

<script type="text/javascript">

$('paginationControl').observe('change', function() {

    window.location = this.options[this.selectedIndex].value;

})

</script>

*/?>

 

 替换application/views/scripts/posts/add.phtml 

<div><?php echo $this->postForm; ?></div>

 

 替换application/views/scripts/posts/edit.phtml

 

<h2>Edit Post</h2>

<div><?php echo $this->postForm; ?></div>

 

 

 替换application/views/scripts/posts/view.phtml 

<div class="title"><?php echo $this->post['Title']; ?></div>

<div class="description"><?php echo $this->post['Description'];  ?></div>

<?php

    $acl = new Model_Acl(); 

    $identity = Zend_Auth::getInstance()->getIdentity();

    if( Zend_Auth::getInstance()->hasIdentity() && $acl->isAllowed( $identity['role'] ,'posts','edit') ) : ?>

<div><a href="<?php echo $this->baseurl().$this->edit; ?>">Edit</a></div> 

<?php endif; ?> 

<div class="comments">

    <?php if( count($this->comments) ) : ?>

        <?php foreach( $this->comments as $comment ) : ?>

            <div class="postedby"><a href="<?php echo $comment['Webpage']; ?>" ><?php echo $this->escape( $comment['Name'] ); ?></a> on <span><?php echo $this->escape( date( 'd-m-Y', strtotime($comment['Postedon']) ) ); ?></span></div>

            <div class="comment"><?php echo $this->escape( $comment['Description'] ); ?></div>

        <?php endforeach; ?>

    <?php else : ?>

    <div>No comments</div>

    <?php endif; ?>

</div>

<div class="newcomments">

<?php if( Zend_Auth::getInstance()->hasIdentity() ) : 

        echo $this->commentsForm;

    else :

        ?>Login to Post comments<?php 

    endif;

?>

</div>

我希望我没漏掉什么。如果有什么错误请通知我。你可以从git获得一个工作副本。我参考了超过50个博客程序。学Zend Framework的同时我还做过一个自定义搜索引擎。我之前的文章里发过。

感谢各位,我希望你们喜欢我的小博客。如果有那里不清楚,请告诉我。你可以发邮件给我,当然最好就在下面发表评论,你可以从http://github.com/harikt/zendblog获得一份代码,而且要象http://zendcasts.com的Jon Lembord一般大言不惭地说:谢谢github和git提供这样精彩的东东。

原文:http://www.harikt.com/content/simple-blog-using-zend-framework-19

Avatar_small
weir 说:
2010年8月16日 00:52

thx for u code.

Avatar_small
路过 说:
2010年9月20日 00:21

有分页,有生成表单,就是没有多文件上传。。。。

Avatar_small
neo 说:
2011年3月24日 17:16

请问你这个博客是zf的吗?

Avatar_small
xqpm 说:
2011年4月20日 15:58

呵呵,很不错的例子


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter
Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee