Zend Framework 入门教程
Last Update: Mar. 11, 2006
mod_rewrite
.
tar.gz
or zip
download, or you can use the command line:
$ wget http://framework.zend.com/download/tgz
$ tar -xvzf ZendFramework-0.1.2.tar.gz
library
directory and place it somewhere convenient. In this tutorial, I rename library
to lib
to provide a clean and simple directory structure:
app/
views/
controllers/
www/
.htaccess
index.php
lib/
www
directory is the document root, controllers
and views
are empty directories you'll use later, and the lib
directory is from the preview release download.
Zend_Controller
. In many ways, it provides the foundation of the application you're developing, and it's also part of what makes the Zend Framework more than just a collection of components. Before you can use it, however, you need to direct all incoming requests to a single PHP script. This tutorial uses mod_rewrite
for this purpose.
mod_rewrite
is an art in itself, but luckily, this particular task is very simple. If you're unfamiliar with mod_rewrite
or configuring Apache in general, create a .htaccess
file in the document root and add the following directives:
RewriteEngine on
RewriteRule !/.(js|ico|gif|jpg|png|css)$ index.php
Zend_Controller
is to remove the mod_rewrite
dependency. In order to provide an example that works with the preview release, this tutorial uses mod_rewrite
.
httpd.conf
directly, you must restart the web server. Otherwise, if you use a .htaccess
file, you should be good to go. You can quickly test this by placing some identifiable text in index.php
and making a request for an arbitrary path such as /foo/bar
. For example, if your host is example.org
, request the URL http://example.org/foo/bar.
include_path
to include the path to the framework library. You can do this in php.ini
, or you can just put the following directive in your .htaccess
file:
php_value include_path "/path/to/lib"
Zend
class contains a collection of static methods that are universally useful. This is the only class you must include manually:
<?php
include
'Zend.php'
;
?>
Zend.php
, you have access to all of the methods from the Zend
class. Loading other classes is simplified with the loadClass()
method. For example, to load the Zend_Controller_Front
class:
<?php
include
'Zend.php'
;
Zend
::
loadClass
(
'Zend_Controller_Front'
);
?>
loadclass()
method is include_path
aware, and it also understands the organization and directory structure of the framework. I use it to load all other classes.
Zend_Controller_Front
, a front controller. In order to begin understanding how it works, place the following code in your index.php
file:
<?php
include
'Zend.php'
;
Zend
::
loadClass
(
'Zend_Controller_Front'
);
$controller
=
Zend_Controller_Front
::
getInstance
();
$controller
->
setControllerDirectory
(
'/path/to/controllers'
);
$controller
->
dispatch
();
?>
<?php
include
'Zend.php'
;
Zend
::
loadClass
(
'Zend_Controller_Front'
);
$controller
=
Zend_Controller_Front
::
getInstance
()
->
setControllerDirectory
(
'/path/to/controllers'
)
->
dispatch
();
?>
/foo/bar
, you get an error. That's good! It lets you know something is happening. The major complaint is that IndexController.php
is not found.
/foo/bar
, foo
is the controller, and bar
is the action. The default value for each is index
.
foo
is the controller, the framework looks for a file called FooController.php
in the controllers
directory. Because this does not exist, the framework falls back to IndexController.php
. Not finding either, it reports the error.
IndexController.php
in the controllers
directory (which you set with setControllerDirectory()
):
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
class
IndexController
extends
Zend_Controller_Action
{
public function
indexAction
()
{
echo
'IndexController::indexAction()'
;
}
}
?>
IndexController
class handles requests for which the controller is index
or for which the indicated controller does not exist, as just explained. The indexAction()
method handles requests for which the action is index
. Remember that index
is the default value for both the controller and the action. If you try a request for /
, /index
, or /index/index
, the indexAction()
method is executed. (Trailing slashes do not alter this behavior.) A request for any other resource is going to result in an error.
IndexController
before continuing. The noRouteAction()
method is called whenever a request is made for a controller that doesn't exist. For example, a request for /foo/bar
executes noRouteAction()
if FooController.php
does not exist. However, a request for /index/foo
still results in an error, because foo
is the action, not the controller.
noRouteAction()
to IndexController.php
:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
class
IndexController
extends
Zend_Controller_Action
{
public function
indexAction
()
{
echo
'IndexController::indexAction()'
;
}
public function
noRouteAction
()
{
$this
->
_redirect
(
'/'
);
}
}
?>
$this->_redirect('/')
to illustrate a possible action to take in noRouteAction()
. This causes requests for nonexistent controllers to be redirected to the root document (front page).
FooController.php
:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
class
FooController
extends
Zend_Controller_Action
{
public function
indexAction
()
{
echo
'FooController::indexAction()'
;
}
public function
barAction
()
{
echo
'FooController::barAction()'
;
}
}
?>
/foo/bar
, you should see that barAction()
is being executed, because bar
is the action. Not only can you already support friendly URLs, but you can also do so in a very organized way with just a few lines of code. Cool!
__call()
method to handle requests for undefined actions such as /foo/baz
:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
class
FooController
extends
Zend_Controller_Action
{
public function
indexAction
()
{
echo
'FooController::indexAction()'
;
}
public function
barAction
()
{
echo
'FooController::barAction()'
;
}
public function
__call
(
$action
,
$arguments
)
{
echo
'FooController:__call()'
;
}
}
?>
Zend_View
is a class that helps you organize your view logic. It is template-system agnostic, and for the sake of simplicity, I don't use a template system in this tutorial. You're free to use one if you prefer.
Zend_View
, change the code in IndexController.php
as follows:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
Zend
::
loadClass
(
'Zend_View'
);
class
IndexController
extends
Zend_Controller_Action
{
public function
indexAction
()
{
$view
= new
Zend_View
();
$view
->
setScriptPath
(
'/path/to/views'
);
echo
$view
->
render
(
'example.php'
);
}
public function
noRouteAction
()
{
$this
->
_redirect
(
'/'
);
}
}
?>
example.php
in the views
directory:
<html>
<head>
<title>This Is an Example</title>
</head>
<body>
<p>This is an example.</p>
</body>
</html>
example.php
. This isn't very useful yet, but keep in mind that you're working toward a very structured and organized way to develop web applications.
Zend_View
a bit clearer, modify your template ( example.php
) to include some data:
<html>
<head>
<title>
<?php
echo
$this
->
escape
(
$this
->
title
);
?>
</title>
</head>
<body>
<?php
echo
$this
->
escape
(
$this
->
body
);
?>
</body>
</html>
$this->escape()
method must be used on all output. Even if you create the output yourself, which is going to be the case in this example, escaping all output is a very good habit that can help you prevent cross-site scripting (XSS) vulnerabilities by default.
$this->title
and $this->body
properties exist to demonstrate dynamic data. These should be defined in the controller, so modify IndexController.php
to assign them:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
Zend
::
loadClass
(
'Zend_View'
);
class
IndexController
extends
Zend_Controller_Action
{
public function
indexAction
()
{
$view
= new
Zend_View
();
$view
->
setScriptPath
(
'/path/to/views'
);
$view
->
title
=
'Dynamic Title'
;
$view
->
body
=
'This is a dynamic body.'
;
echo
$view
->
render
(
'example.php'
);
}
public function
noRouteAction
()
{
$this
->
_redirect
(
'/'
);
}
}
?>
$this
in the template is that it is executed within the scope of the Zend_View
instance.
example.php
is just an ordinary PHP script, so you can do anything you want. Just try to be disciplined enough to limit your use of templates to only what is required to display data. Your controller (or a module to which the controller dispatches) should handle all of your business logic.
Zend_View
before continuing. Instantiating the $view
object within each method of the controller requires a lot of extra typing, and our primary goal is to make it easier to quickly develop web applications. It's also a hassle to call setScriptPath()
in each case if all of the templates reside in a single directory.
Zend
class includes a registry that helps you eliminate this overhead. You can store your $view
object in the registry by using the register()
method:
<?php
Zend
::
register
(
'view'
,
$view
);
?>
registry()
method:
<?php
$view
=
Zend
::
registry
(
'view'
);
?>
Zend_InputFilter
. This class provides a simple but rigid approach to input filtering. You instantiate it by passing an array of data to be filtered:
<?php
$filterPost
= new
Zend_InputFilter
(
$_POST
);
?>
$_POST
) to NULL
, so direct access is no longer possible. Zend_InputFilter
instead provides a small, focused collection of methods that filter data according to specific criteria. For example, if you want the alphabetic characters of $_POST['name'
], you can use the getAlpha()
method:
<?php
/* $_POST['name'] = 'John123Doe'; */
$filterPost
= new
Zend_InputFilter
(
$_POST
);
/* $_POST = NULL; */
$alphaName
=
$filterPost
->
getAlpha
(
'name'
);
/* $alphaName = 'JohnDoe'; */
?>
$filterPost
in this example) is a protected cage that contains the tainted data, making access to that data more controlled and consistent. Therefore, you should always use Zend_InputFilter
when you need to access input.
Zend_Filter
provides static filtering methods that follow the same conventions as the Zend_InputFilter
methods.
/
/add/news
/add/comment
/admin
/admin/approve
/view/{id}
IndexController
lists the news, the AddController
handles adding news and comments, the AdminController
handles administrative actions such as approving news, and the ViewController
handles viewing a specific news entry and its corresponding comments.
FooController.php
if it still exists, and modify IndexController.php
to add the appropriate actions and some comments as placeholders for the business logic:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
class
IndexController
extends
Zend_Controller_Action
{
public function
indexAction
()
{
/* List the news. */
}
public function
noRouteAction
()
{
$this
->
_redirect
(
'/'
);
}
}
?>
AddController.php
:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
class
AddController
extends
Zend_Controller_Action
{
function
indexAction
()
{
$this
->
_redirect
(
'/'
);
}
function
commentAction
()
{
/* Add a comment. */
}
function
newsAction
()
{
/* Add news. */
}
function
__call
(
$action
,
$arguments
)
{
$this
->
_redirect
(
'/'
);
}
}
?>
indexAction()
method of AddController
should never be called. This only happens when the requested path is /add
. Because a user might explore the URLs manually, this is likely, so you can redirect the user to the front page, display an error, or take whatever action you feel is appropriate.
AdminController.php
:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
class
AdminController
extends
Zend_Controller_Action
{
function
indexAction
()
{
/* Display admin interface. */
}
function
approveAction
()
{
/* Approve news. */
}
function
__call
(
$action
,
$arguments
)
{
$this
->
_redirect
(
'/'
);
}
}
?>
ViewController.php
:
<?php
Zend
::
loadClass
(
'Zend_Controller_Action'
);
class
ViewController
extends
Zend_Controller_Action
{
function
indexAction
()
{
$this
->
_redirect
(
'/'
);
}
function
__call
(
$id
,
$arguments
)
{
/* Display news and comments for $id. */
}
}
?>
AddController
, the index()
method should never be called, so you can take whatever action you feel is appropriate. ViewController
is a bit different than the others, because you don't know what the valid actions are. In order to support URLs like /view/23
, you must support dynamic actions with __call()
.
<?php
class
Database
{
private
$_db
;
public function
__construct
(
$filename
)
{
$this
->
_db
= new
SQLiteDatabase
(
$filename
);
}
public function
addComment
(
$name
,
$comment
,
$newsId
)
{
$name
=
sqlite_escape_string
(
$name
);
$comment
=
sqlite_escape_string
(
$comment
);
$newsId
=
sqlite_escape_string
(
$newsId
);
$sql
=
"INSERT
INTO comments (name, comment, newsId)
VALUES ('$name', '$comment', '$newsId')"
;
return
$this
->
_db
->
query
(
$sql
);
}
public function
addNews
(
$title
,
$content
)
{
$title
=
sqlite_escape_string
(
$title
);
$content
=
sqlite_escape_string
(
$content
);
$sql
=
"INSERT
INTO news (title, content)
VALUES ('$title', '$content')"
;
return
$this
->
_db
->
query
(
$sql
);
}
public function
approveNews
(
$ids
)
{
foreach (
$ids
as
$id
) {
$id
=
sqlite_escape_string
(
$id
);
$sql
=
"UPDATE news
SET approval = 'T'
WHERE id = '$id'"
;
if (!
$this
->
_db
->
query
(
$sql
)) {
return
FALSE
;
}
}
return
TRUE
;
}
public function
getComments
(
$newsId
)
{
$newsId
=
sqlite_escape_string
(
$newsId
);
$sql
=
"SELECT name, comment
FROM comments
WHERE newsId = '$newsId'"
;
if (
$result
=
$this
->
_db
->
query
(
$sql
)) {
return
$result
->
fetchAll
();
}
return
FALSE
;
}
public function
getNews
(
$id
=
'ALL'
)
{
$id
=
sqlite_escape_string
(
$id
);
switch (
$id
) {
case
'ALL'
:
$sql
=
"SELECT id,
title
FROM news
WHERE approval = 'T'"
;
break;
case
'NEW'
:
$sql
=
"SELECT *
FROM news
WHERE approval != 'T'"
;
break;
default:
$sql
=
"SELECT *
FROM news
WHERE id = '$id'"
;
break;
}
if (
$result
=
$this
->
_db
->
query
(
$sql
)) {
if (
$result
->
numRows
() !=
1
) {
return
$result
->
fetchAll
();
} else {
return
$result
->
fetch
();
}
}
return
FALSE
;
}
}
?>
<?php
$db
= new
SQLiteDatabase
(
'/path/to/db.sqlite'
);
$db
->
query
(
"CREATE TABLE news (
id INTEGER PRIMARY KEY,
title VARCHAR(255),
content TEXT,
approval CHAR(1) DEFAULT 'F'
)"
);
$db
->
query
(
"CREATE TABLE comments (
id INTEGER PRIMARY KEY,
name VARCHAR(255),
comment TEXT,
newsId INTEGER
)"
);
?>
Database
class:
<?php
$db
= new
Database
(
'/path/to/db.sqlite'
);
?>
Database.php
in the lib
directory, and loadClass()
will be able to find it. Your index.php
file should now instantiate $view
and $db
and store them in the registry. You can also create a function called __autoload()
to automatically handle loading all the classes you need:
<?php
include
'Zend.php'
;
function
__autoload
(
$class
)
{
Zend
::
loadClass
(
$class
);
}
$db
= new
Database
(
'/path/to/db.sqlite'
);
Zend
::
register
(
'db'
,
$db
);
$view
= new
Zend_View
;
$view
->
setScriptPath
(
'/path/to/views'
);
Zend
::
register
(
'view'
,
$view
);
$controller
=
Zend_Controller_Front
::
getInstance
()
->
setControllerDirectory
(
'/path/to/controllers'
)
->
dispatch
();
?>
views
directory. The index.php
file can be used for displaying the index view:
<html>
<head>
<title>News</title>
</head>
<body>
<h1>News</h1>
<?php
foreach (
$this
->
news
as
$entry
) {
?>
<p>
<a href="/view/
<?php
echo
$this
->
escape
(
$entry
[
'id'
]);
?>
">
<?php
echo
$this
->
escape
(
$entry
[
'title'
]);
?>
</a>
</p>
<?php
}
?>
<h1>Add News</h1>
<form action="/add/news" method="POST">
<p>Title:<br /><input type="text" name="title" /></p>
<p>Content:<br /><textarea name="content"></textarea></p>
<p><input type="submit" value="Add News" /></p>
</form>
</body>
</html>
view.php
template can be used for displaying a specific news entry:
<html>
<head>
<title>
<?php
echo
$this
->
escape
(
$this
->
news
[
'title'
]);
?>
</title>
</head>
<body>
<h1>
<?php
echo
$this
->
escape
(
$this
->
news
[
'title'
]);
?>
</h1>
<p>
<?php
echo
$this
->
escape
(
$this
->
news
[
'content'
]);
?>
</p>
<h1>Comments</h1>
<?php
foreach (
$this
->
comments
as
$comment
) {
?>
<p>
<?php
echo
$this
->
escape
(
$comment
[
'name'
]);
?>
writes:
</p>
<blockquote>
<?php
echo
$this
->
escape
(
$comment
[
'comment'
]);
?>
</blockquote>
<?php
}
?>
<h1>Add a Comment</h1>
<form action="/add/comment" method="POST">
<input type="hidden" name="newsId"
value="
<?php
echo
$this
->
escape
(
$this
->
id
);
?>
" />
<p>Name:<br /><input type="text" name="name" /></p>
<p>Comment:<br /><textarea name="comment"></textarea></p>
<p><input type="submit" value="Add Comment" /></p>
</form>
</body>
</html>
admin.php
template can be used to approve news entries:
<html>
<head>
<title>News Admin</title>
</head>
<body>
<form action="/admin/approve" method="POST">
<?php
foreach (
$this
->
news
as
$entry
) {
?>
<p>
<input type="checkbox" name="ids[]"
value="
<?php
echo
$this
->
escape
(
$entry
[
'id'
]);
?>
" />
<?php
echo
$this
->
escape
(
$entry
[
'title'
]);
?>
<?php
echo
$this
->
escape
(
$entry
[
'content'
]);
?>
</p>
<?php
}
?>
<p>
Password:<br /><input type="password" name="password" />
</p>
<p><input type="submit" value="Approve" /></p>
</form>
</body>
</html>
IndexController.php
becomes the following:
<?php
class
IndexController
extends
Zend_Controller_Action
{
public function
indexAction
()
{
/* List the news. */
$db
=
Zend
::
registry
(
'db'
);
$view
=
Zend
::
registry
(
'view'
);
$view
->
news
=
$db
->
getNews
();
echo
$view
->
render
(
'index.php'
);
}
public function
noRouteAction
()
{
$this
->
_redirect
(
'/'
);
}
}
?>
AddController.php
is a bit more involved, so it requires more code:
<?php
class
AddController
extends
Zend_Controller_Action
{
function
indexAction
()
{
$this
->
_redirect
(
'/'
);
}
function
commentAction
()
{
/* Add a comment. */
$filterPost
= new
Zend_InputFilter
(
$_POST
);
$db
=
Zend
::
registry
(
'db'
);
$name
=
$filterPost
->
getAlpha
(
'name'
);
$comment
=
$filterPost
->
noTags
(
'comment'
);
$newsId
=
$filterPost
->
getDigits
(
'newsId'
);
$db
->
addComment
(
$name
,
$comment
,
$newsId
);
$this
->
_redirect
(
"/view/$newsId"
);
}
function
newsAction
()
{
/* Add news. */
$filterPost
= new
Zend_InputFilter
(
$_POST
);
$db
=
Zend
::
registry
(
'db'
);
$title
=
$filterPost
->
noTags
(
'title'
);
$content
=
$filterPost
->
noTags
(
'content'
);
$db
->
addNews
(
$title
,
$content
);
$this
->
_redirect
(
'/'
);
}
function
__call
(
$action
,
$arguments
)
{
$this
->
_redirect
(
'/'
);
}
}
?>
AdminController.php
, you handle two actions, displaying the admin interface and approving news:
<?php
class
AdminController
extends
Zend_Controller_Action
{
function
indexAction
()
{
/* Display admin interface. */
$db
=
Zend
::
registry
(
'db'
);
$view
=
Zend
::
registry
(
'view'
);
$view
->
news
=
$db
->
getNews
(
'NEW'
);
echo
$view
->
render
(
'admin.php'
);
}
function
approveAction
()
{
/* Approve news. */
$filterPost
= new
Zend_InputFilter
(
$_POST
);
$db
=
Zend
::
registry
(
'db'
);
if (
$filterPost
->
getRaw
(
'password'
) ==
'mypass'
) {
$db
->
approveNews
(
$filterPost
->
getRaw
(
'ids'
));
$this
->
_redirect
(
'/'
);
} else {
echo
'The password is incorrect.'
;
}
}
function
__call
(
$action
,
$arguments
)
{
$this
->
_redirect
(
'/'
);
}
}
?>
ViewController.php
:
<?php
class
ViewController
extends
Zend_Controller_Action
{
function
indexAction
()
{
$this
->
_redirect
(
'/'
);
}
function
__call
(
$id
,
$arguments
)
{
/* Display news and comments for $id. */
$id
=
Zend_Filter
::
getDigits
(
$id
);
$db
=
Zend
::
registry
(
'db'
);
$view
=
Zend
::
registry
(
'view'
);
$view
->
news
=
$db
->
getNews
(
$id
);
$view
->
comments
=
$db
->
getComments
(
$id
);
$view
->
id
=
$id
;
echo
$view
->
render
(
'view.php'
);
}
}
?>
Zend Framework 入门教程相关推荐
- Zend Framework 入门教程(简体中文版)
Zend Framework 入门教程(简体中文版) Getting Started With the Zend Framework Author:Rob Allen, www.akrabat.com ...
- Zend Framework入门教程
转战了各个Blog平台,发现还是落户到csdn上吧,以后吐槽在新浪,发长文在csdn,整理链接在Baidu Hi 在大四的时候接下的实验室网站,网站是上三届的吴师兄写的,主要采用的是LAMP,其中PH ...
- php zend framework 教程,Zend Framework入门教程之Zend_Session会话操作详解
本文实例讲述了Zend Framework入门教程之Zend_Session会话操作.分享给大家供大家参考,具体如下: 会话命名空间 实现会话 代码: require_once "Zend/ ...
- Zend Framework实例教程
Zend Framework实例教程(1) 2007-03-24 18:23:50 我们邀请PHP安全专家 - 最新版Zend Frame的贡献者 - Chris Shiflett帮我们写一篇文章介绍 ...
- Swoole Framework 入门教程(1)
Swoole Framework入门教程1 简介 Swoole为广大php 程序员带来了福音, 用一句话说 重新定义PHP, 底层用c扩展实现,配合PHP7 性能不弱于 golang ,并且还可以脱离 ...
- php zend框架入门,Zend Framework 入门——快速上手
1. 安装 从 Zend Framework 的网页上下载最新版本.解压后,把整个目录拷贝到一个理想的地方,比如:/php/library/Zend. 打开 php.ini 文件,确认包含 Zend ...
- Zend Framework实例教程三
整合 为了进行整合,在lib目录下创建Database.php,loadClass()就可以找到它.你的index.php文件现在就会初始化$view和$db并存储到寄存器.你也可以创建__autol ...
- Zend Framework 入门(1)—快速上手
1. 安装 从 Zend Framework 的网页上下载最新版本.解压后,把整个目录拷贝到一个理想的地方,比如:/php/library/Zend. 打开 php.ini 文件,确认包含 Zend ...
- Entity Framework入门教程:创建实体数据模型
下图为一个已经创建好的数据库表关系 实体数据模型的创建过程 在Visual Studio项目中,右键程序集菜单,选择[添加]->[新建项],在[添加新项窗口]中选择[ADO.NET实体数据模型] ...
最新文章
- 【Android开发】基本组件-复选框
- php中一个经典的!==的用法
- Simulating Ocean Water (2) (2005-03-31 update)
- k8s之informer简单理解
- 面向对象2(构造方法、抽象类、接口)
- lighttpd mysql_lighttpd+mysql+php
- 问题 D: 二叉树求高度
- 过滤你不想看见的网页里的东西
- 【SpringBoot】spring boot + mybatis + druid
- python通过函数类属性_函数作为类属性的赋值如何成为Python中的一个方法?
- 第一百六十三天 how can I 坚持
- url 的正则表达式:path-to-regexp
- ERP必须基于企业流程管理
- 庆祝kkkbo出道!
- 黑苹果 dmg,cdr和iso的区别
- 飞机大战,坦克大战源码、简单仿记事本、错题本源码及笔记
- 基于Python的家谱信息管理系统
- 浏览器架构的误区和瘦客户端应用
- siteground主机黑五优惠最低2折-2.99美元每月-vps主机-WordPress主机服务器
- 苏州最新税前税后计算机,苏州税前税后工资计算标准
热门文章
- 腾讯AI Lab「云深」与成都先导合作「AI+药物」,发布骨架跃迁新算法
- 职场跃迁--这4个坎必须迈过
- raspberry 使用EDUP无线网卡上网
- 编译原理短语、直接短语及句柄
- 比特币量化交易:利用Python构建自己的算法交易系统
- 中国有多少java程序员_中国有多少个程序员?
- linux malloc 返回指针不可用,为啥malloc后得到的指针不可以free ??? 谢谢 [已解决]...
- ecshop二次开发目录文档【文件结构说明和数据库表分析】
- jquery仿淘宝SKU选择商品属性代码
- 疫情信息查询微信小程序的实现