Home > Article > Backend Development > Yii learning (5)--Active Record association, yii--active_PHP tutorial
Official website original text: http://www.yiiframework.com/doc/guide/1.1 /zh_cn/database.arr
The second half of the official website is in English, and the Chinese content is less than the English content. I will put it here first and translate it later when I have time.
We have seen how to use Active Record (AR) to get data from a single data table. In this section, we explain how to use AR to connect multiple related data tables and retrieve the joined data set.
In order to use relational AR, we recommend defining primary key-foreign key constraints in the tables that need to be related. These constraints can help ensure the consistency and integrity of related data.
For simplicity, we demonstrate the examples in this section using the data structure in an Entity-Relationship (ER) diagram as shown below.
Information: Support for foreign key constraints varies between DBMSs. SQLite
Before we use AR to perform related queries, we need to let AR know how one AR class is related to another.
The relationship between two AR classes is directly related through the relationship between the data tables represented by the AR classes. From a database perspective, there are three types of relationships between tables A and B: one-to-many (such as tbl_user
and tbl_post
), one-to-one (such as tbl_user
and tbl_profile
) and many-to-many (such as tbl_category
and tbl_post
). In AR, there are four types of relationships:
BELONGS_TO
(belongs to): If the relationship between tables A and B is one-to-many, then table B belongs to table A (for example, Post
belongs to User
);
HAS_MANY
(there are multiple): If the relationship between tables A and B is one-to-many, then A has multiple B (for example, User
has multiple Post
);
HAS_ONE
(has one): This is a special case of HAS_MANY
, A has at most one B (for example, User
has at most one Profile
);
MANY_MANY
: This corresponds to the many-to-many relationship in the database. Since most DBMS do not directly support many-to-many relationships, a relationship table is required to split the many-to-many relationship into a one-to-many relationship. In our example data structure, tbl_post_category
is used for this purpose. In AR terms, we can interpret MANY_MANY
as a combination of BELONGS_TO
and HAS_MANY
. For example, Post
belongs to many (belongs to many) Category
, Category
has multiple (has many) Post
.
To define relationships in AR, you need to override the relationships() method in CActiveRecord. This method returns an array of relationship configurations. Each array element represents a single relationship in the following format.
'VarName'=><span>array</span>('RelationType', 'ClassName', 'ForeignKey', ...additional options)
where VarName
is the name of the relationship; RelationType
specifies the relationship type, which can be one of the following four constants: self::BELONGS_TO
,self::HAS_ONE
, self::HAS_MANY
and self::MANY_MANY
; ClassName
is this AR The name of the AR class to which the class is associated; ForeignKey
specifies the foreign key (one or more) used in the relationship. Additional options can be specified at the end of each relationship (more on this later).
The following code demonstrates how to define the relationship between User
and Post
classes:
<span>class</span> Post <span>extends</span><span> CActiveRecord { </span>...... <span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'author'=><span>array</span>(self::BELONGS_TO, 'User', 'author_id'), 'categories'=><span>array</span>(self::MANY_MANY, 'Category', 'tbl_post_category(post_id, category_id)'),<span> ); } } </span><span>class</span> User <span>extends</span><span> CActiveRecord { </span>...... <span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'posts'=><span>array</span>(self::HAS_MANY, 'Post', 'author_id'), 'profile'=><span>array</span>(self::HAS_ONE, 'Profile', 'owner_id'),<span> ); } }</span>
Information: Foreign keys may be compound, containing two or more columns. In this case, we should link these foreign key names, separated by spaces or commas. For the MANY_MANY
relationship type, the name of the associated table must also be specified in the foreign key. For example, the Post
relationship in categories
is specified by the foreign key tbl_post_category(post_id, category_id)
.
Relationship definitions in an AR class implicitly add an attribute to the class for each relationship. After a correlation query is executed, the corresponding properties will be populated with the associated AR instance. For example, if $author
represents a User
AR instance, we can use $author->posts
to access its associated Post
instance.
执行关联查询最简单的方法是读取一个 AR 实例中的关联属性。如果此属性以前没有被访问过,则一个关联查询将被初始化,它将两个表关联并使用当前 AR 实例的主键过滤。 查询结果将以所关联 AR 类的实例的方式保存到属性中。这就是传说中的 懒惰式加载(lazy loading,也可译为 迟加载) 方式,例如,关联查询只在关联的对象首次被访问时执行。 下面的例子演示了怎样使用这种方式:
<span>//</span><span> 获取 ID 为 10 的帖子</span> <span>$post</span>=Post::model()->findByPk(10<span>); </span><span>//</span><span> 获取帖子的作者(author): 此处将执行一个关联查询。</span> <span>$author</span>=<span>$post</span>->author;
信息: 如果关系中没有相关的实例,则相应的属性将为 null 或一个空数组。 BELONGS_TO
和 HAS_ONE
关系的结果是 null, HAS_MANY
和 MANY_MANY
的结果是一个空数组。 注意, HAS_MANY
和MANY_MANY
关系返回对象数组,你需要在访问任何属性之前先遍历这些结果。 否则,你可能会收到 "Trying to get property of non-object(尝试访问非对象的属性)" 错误。
懒惰式加载用起来很方便,但在某些情况下并不高效。如果我们想获取 N
个帖子的作者,使用这种懒惰式加载将会导致执行 N
个关联查询。 这种情况下,我们应该改为使用 渴求式加载(eager loading)方式。
渴求式加载方式会在获取主 AR 实例的同时获取关联的 AR 实例。 这是通过在使用 AR 中的 find 或 findAll 方法时配合使用 with 方法完成的。例如:
<span>$posts</span>=Post::model()->with('author')->findAll();
上述代码将返回一个 Post
实例的数组。与懒惰式加载方式不同,在我们访问每个 Post
实例中的 author
属性之前,它就已经被关联的 User
实例填充了。 渴求式加载通过 一个 关联查询返回所有帖子及其作者,而不是对每个帖子执行一次关联查询。
我们可以在 with() 方法中指定多个关系名字,渴求式加载将一次性全部取回他们。例如,如下代码会将帖子连同其作者和分类一并取回。
<span>$posts</span>=Post::model()->with('author','categories')->findAll();
我们也可以实现嵌套的渴求式加载。像下面这样, 我们传递一个分等级的关系名表达式到 with() 方法,而不是一个关系名列表:
<span>$posts</span>=Post::model()-><span>with( </span>'author.profile', 'author.posts', 'categories')->findAll();
上述示例将取回所有帖子及其作者和所属分类。它还同时取回每个作者的简介(author.profile)和帖子(author.posts)。
从版本 1.1.0 开始,渴求式加载也可以通过指定 CDbCriteria::with 的属性执行,就像下面这样:
<span>$criteria</span>=<span>new</span><span> CDbCriteria; </span><span>$criteria</span>->with=<span>array</span><span>( </span>'author.profile', 'author.posts', 'categories',<span> ); </span><span>$posts</span>=Post::model()->findAll(<span>$criteria</span>);
或者
<span>$posts</span>=Post::model()->findAll(<span>array</span><span>( </span>'with'=><span>array</span><span>( </span>'author.profile', 'author.posts', 'categories',<span> ) );</span>
我们提到在关系声明时可以指定附加的选项。这些 名-值 对形式的选项用于自定义关系型查询。概括如下:
select
: 关联的 AR 类中要选择(select)的列的列表。 默认为 '*',即选择所有列。此选项中的列名应该是已经消除歧义的。
condition
: 即 WHERE
条件。默认为空。此选项中的列名应该是已经消除歧义的。
params
: 要绑定到所生成的 SQL 语句的参数。应该以 名-值 对数组的形式赋值。此选项从 1.0.3 版起有效。
on
: 即 ON
语句。此处指定的条件将会通过 AND
操作符附加到 join 条件中。此选项中的列名应该是已经消除歧义的。 此选项不会应用到 MANY_MANY
关系中。此选项从 1.0.2 版起有效。
order
: 即 ORDER BY
语句。默认为空。 此选项中的列名应该是已经消除歧义的。
with
: a list of child related objects that should be loaded together with this object. Be aware that using this option inappropriately may form an infinite relation loop.
joinType
: type of join for this relationship. It defaults to LEFT OUTER JOIN
.
alias
: the alias for the table associated with this relationship. It defaults to null, meaning the table alias is the same as the relation name.
together
: whether the table associated with this relationship should be forced to join together with the primary table and other tables. This option is only meaningful for HAS_MANY
and MANY_MANY
relations. If this option is set false, the table associated with the HAS_MANY
or MANY_MANY
relation will be joined with the primary table in a separate SQL query, which may improve the overall query performance since less duplicated data is returned. If this option is set true, the associated table will always be joined with the primary table in a single SQL query, even if the primary table is paginated. If this option is not set, the associated table will be joined with the primary table in a single SQL query only when the primary table is not paginated. For more details, see the section "Relational Query Performance".
join
: the extra JOIN
clause. It defaults to empty. This option has been available since version 1.1.3.
group
: the GROUP BY
clause. It defaults to empty. Column names referenced in this option should be disambiguated.
having
: the HAVING
clause. It defaults to empty. Column names referenced in this option should be disambiguated.
index
: the name of the column whose values should be used as keys of the array that stores related objects. Without setting this option, an related object array would use zero-based integer index. This option can only be set for HAS_MANY
and MANY_MANY
relations.
scopes
: scopes to apply. In case of a single scope can be used like 'scopes'=>'scopeName'
, in case of multiple scopes can be used like 'scopes'=>array('scopeName1','scopeName2')
. This option has been available since version 1.1.9.
In addition, the following options are available for certain relationships during lazy loading:
limit
: limit of the rows to be selected. This option does NOT apply to BELONGS_TO
relation.
offset
: offset of the rows to be selected. This option does NOT apply to BELONGS_TO
relation.
through
: name of the model's relation that will be used as a bridge when getting related data. This option has been available since version 1.1.7 where it can be used for HAS_ONE
and HAS_MANY
. Since 1.1.14 it can be used for BELONGS_TO
as well.
Below we modify the posts
relationship declaration in the User
by including some of the above options:
<span>class</span> User <span>extends</span><span> CActiveRecord { </span><span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'posts'=><span>array</span>(self::HAS_MANY, 'Post', 'author_id', 'order'=>'posts.create_time DESC', 'with'=>'categories'), 'profile'=><span>array</span>(self::HAS_ONE, 'Profile', 'owner_id'),<span> ); } }</span>
Now if we access $author->posts
, we would obtain the author's posts sorted according to their creation time in descending order. Each post instance also has its categories loaded.
When a column name appears in two or more tables being joined together, it needs to be disambiguated. This is done by prefixing the column name with its table's alias name.
In relational AR query, the alias name for the primary table is fixed as t
, while the alias name for a relational table is the same as the corresponding relation name by default. For example, in the following statement, the alias name for Post
and Comment
is t
and comments
, respectively:
<span>$posts</span>=Post::model()->with('comments')->findAll();
Now assume both Post
and Comment
have a column called create_time
indicating the creation time of a post or comment, and we would like to fetch posts together with their comments by ordering first the posts' creation time and then the comments' creation time. We need to disambiguate the create_time
column like the following:
<span>$posts</span>=Post::model()->with('comments')->findAll(<span>array</span><span>( </span>'order'=>'t.create_time, comments.create_time'<span> ));</span>
Tip: The default alias of a related table is the name of the relation. Please note that if you're using relation from within another relation the alias will be the former relation name only and will not be prefixed with the parent relation. For example, the alias for 'author.group' relation is 'group', not 'author.group'.
<span>$posts</span>=Post::model()->with('author', 'author.group')->findAll(<span>array</span><span>( </span>'order'=>'group.name, author.name, t.title'<span> ));</span>
You can avoid the collision of tables' aliases by specifying the alias property of the relation.
<span>$comments</span>=Comment::model()-><span>with( </span>'author', 'post', 'post.author'=><span>array</span>('alias'=>'p_author'))->findAll(<span>array</span><span>( </span>'order'=>'author.name, p_author.name, post.title'<span> ));</span>
We can use dynamic relational query options in both with() and the with
option. The dynamic options will overwrite existing options as specified in the relations() method. For example, with the above User
model, if we want to use eager loading approach to bring back posts belonging to an author in ascending order (theorder
option in the relation specification is descending order), we can do the following:
User::model()->with(<span>array</span><span>( </span>'posts'=><span>array</span>('order'=>'posts.create_time ASC'), 'profile',<span> ))</span>->findAll();
Dynamic query options can also be used when using the lazy loading approach to perform relational query. To do so, we should call a method whose name is the same as the relation name and pass the dynamic query options as the method parameter. For example, the following code returns a user's posts whose status
is 1:
<span>$user</span>=User::model()->findByPk(1<span>); </span><span>$posts</span>=<span>$user</span>->posts(<span>array</span>('condition'=>'status=1'));
As we described above, the eager loading approach is mainly used in the scenario when we need to access many related objects. It generates a big complex SQL statement by joining all needed tables. A big SQL statement is preferrable in many cases since it simplifies filtering based on a column in a related table. It may not be efficient in some cases, however.
Consider an example where we need to find the latest posts together with their comments. Assuming each post has 10 comments, using a single big SQL statement, we will bring back a lot of redundant post data since each post will be repeated for every comment it has. Now let's try another approach: we first query for the latest posts, and then query for their comments. In this new approach, we need to execute two SQL statements. The benefit is that there is no redundancy in the query results.
So which approach is more efficient? There is no absolute answer. Executing a single big SQL statement may be more efficient because it causes less overhead in DBMS for parsing and executing the SQL statements. On the other hand, using the single SQL statement, we end up with more redundant data and thus need more time to read and process them.
For this reason, Yii provides the together
query option so that we choose between the two approaches as needed. By default, Yii uses eager loading, i.e., generating a single SQL statement, except when LIMIT
is applied to the primary model. We can set the together
option in the relation declarations to be true to force a single SQL statement even when LIMIT
is used. Setting it to false will result in some of tables will be joined in separate SQL statements. For example, in order to use separate SQL statements to query for the latest posts with their comments, we can declare the comments
relation in Post
class as follows,
<span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'comments' => <span>array</span>(self::HAS_MANY, 'Comment', 'post_id', 'together'=><span>false</span>),<span> ); }</span>
We can also dynamically set this option when we perform the eager loading:
<span>$posts</span> = Post::model()-><span>with( </span><span>array</span>('comments'=><span>array</span><span>( </span>'together'=><span>false</span><span> )) )</span>->findAll();
Besides the relational query described above, Yii also supports the so-called statistical query (or aggregational query). It refers to retrieving the aggregational information about the related objects, such as the number of comments for each post, the average rating for each product, etc. Statistical query can only be performed for objects related in HAS_MANY
(e.g. a post has many comments) or MANY_MANY
(e.g. a post belongs to many categories and a category has many posts).
Performing statistical query is very similar to performing relation query as we described before. We first need to declare the statistical query in the relations() method of CActiveRecord like we do with relational query.
<span>class</span> Post <span>extends</span><span> CActiveRecord { </span><span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'commentCount'=><span>array</span>(self::<span>STAT</span>, 'Comment', 'post_id'), 'categoryCount'=><span>array</span><span>( self</span>::<span>STAT</span>, 'Category', 'post_category(post_id, category_id)'<span> )</span>,<span> ); } }</span>
In the above, we declare two statistical queries: commentCount
calculates the number of comments belonging to a post, and categoryCount
calculates the number of categories that a post belongs to. Note that the relationship between Post
and Comment
is HAS_MANY
, while the relationship between Post
and Category
isMANY_MANY
(with the joining table post_category
). As we can see, the declaration is very similar to those relations we described in earlier subsections. The only difference is that the relation type is STAT
here.
With the above declaration, we can retrieve the number of comments for a post using the expression $post->commentCount
. When we access this property for the first time, a SQL statement will be executed implicitly to retrieve the corresponding result. As we already know, this is the so-called lazy loading approach. We can also use the eager loading approach if we need to determine the comment count for multiple posts:
<span>$posts</span>=Post::model()->with('commentCount', 'categoryCount')->findAll();
The above statement will execute three SQLs to bring back all posts together with their comment counts and category counts. Using the lazy loading approach, we would end up with 2*N+1
SQL queries if there are N
posts.
By default, a statistical query will calculate the COUNT
expression (and thus the comment count and category count in the above example). We can customize it by specifying additional options when we declare it inrelations(). The available options are summarized as below.
select
: the statistical expression. Defaults to COUNT(*)
, meaning the count of child objects.
defaultValue
: the value to be assigned to those records that do not receive a statistical query result. For example, if a post does not have any comments, its commentCount
would receive this value. The default value for this option is 0.
condition
: the WHERE
clause. It defaults to empty.
params
: the parameters to be bound to the generated SQL statement. This should be given as an array of name-value pairs.
order
: the ORDER BY
clause. It defaults to empty.
group
: the GROUP BY
clause. It defaults to empty.
having
: the HAVING
clause. It defaults to empty.
Relational query can also be performed in combination with named scopes. It comes in two forms. In the first form, named scopes are applied to the main model. In the second form, named scopes are applied to the related models.
The following code shows how to apply named scopes to the main model.
<span>$posts</span>=Post::model()->published()->recently()->with('comments')->findAll();
This is very similar to non-relational queries. The only difference is that we have the with()
call after the named-scope chain. This query would bring back recently published posts together with their comments.
And the following code shows how to apply named scopes to the related models.
<span>$posts</span>=Post::model()->with('comments:recently:approved')-><span>findAll(); </span><span>//</span><span> or since 1.1.7</span> <span>$posts</span>=Post::model()->with(<span>array</span><span>( </span>'comments'=><span>array</span><span>( </span>'scopes'=><span>array</span>('recently','approved'<span>) )</span>,<span> ))</span>-><span>findAll(); </span><span>//</span><span> or since 1.1.7</span> <span>$posts</span>=Post::model()->findAll(<span>array</span><span>( </span>'with'=><span>array</span><span>( </span>'comments'=><span>array</span><span>( </span>'scopes'=><span>array</span>('recently','approved'<span>) )</span>,<span> )</span>,<span> ));</span>
The above query will bring back all posts together with their approved comments. Note that comments
refers to the relation name, while recently
and approved
refer to two named scopes declared in the Comment
model class. The relation name and the named scopes should be separated by colons.
Occasionally you may need to retrieve a scoped relationship using a lazy-loading approach, instead of the normal eager loading method shown above. In that case, the following syntax will do what you need:
~~ [php] // note the repetition of the relationship name, which is necessary $approvedComments = $post->comments('comments:approved'); ~~
Named scopes can also be specified in the with
option of the relational rules declared inCActiveRecord::relations(). In the following example, if we access $user->posts
, it would bring back allapproved comments of the posts.
<span>class</span> User <span>extends</span><span> CActiveRecord { </span><span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'posts'=><span>array</span>(self::HAS_MANY, 'Post', 'author_id', 'with'=>'comments:approved'),<span> ); } } </span><span>//</span><span> or since 1.1.7</span> <span>class</span> User <span>extends</span><span> CActiveRecord { </span><span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'posts'=><span>array</span>(self::HAS_MANY, 'Post', 'author_id', 'with'=><span>array</span><span>( </span>'comments'=><span>array</span><span>( </span>'scopes'=>'approved'<span> )</span>,<span> )</span>,<span> )</span>,<span> ); } }</span>
Note: Before 1.1.7 named scopes applied to related models must be specified in CActiveRecord::scopes. As a result, they cannot be parameterized.
Since 1.1.7 it's possible to pass parameters for relational named scopes. For example, if you have scope named rated
in the Post
that accepts minimum rating of post, you can use it from User
the following way:
<span>$users</span>=User::model()->findAll(<span>array</span><span>( </span>'with'=><span>array</span><span>( </span>'posts'=><span>array</span><span>( </span>'scopes'=><span>array</span><span>( </span>'rated'=>5,<span> )</span>,<span> )</span>,<span> )</span>,<span> )); </span><span>class</span> Post <span>extends</span><span> CActiveRecord { </span>...... <span>public</span> <span>function</span> rated(<span>$rating</span><span>) { </span><span>$this</span>->getDbCriteria()->mergeWith(<span>array</span><span>( </span>'condition'=>'rating=:rating', 'params'=><span>array</span>(':rating'=><span>$rating</span>),<span> )); </span><span>return</span> <span>$this</span><span>; } </span>......<span> }</span>
When using through
, relation definition should look like the following:
'comments'=><span>array</span>(self::HAS_MANY,'Comment',<span>array</span>('key1'=>'key2'),'through'=>'posts'),
In the above array('key1'=>'key2')
:
key1
is a key defined in relation specified in through
(posts
is this case).key2
is a key defined in a model relation points to (Comment
in this case).through
can be used with HAS_ONE
, BELONGS_TO
and HAS_MANY
relations.
HAS_MANY
throughHAS_MANY through ER
An example of HAS_MANY
with through
is getting users from a particular group when users are assigned to groups via roles.
A bit more complex example is getting all comments for all users of a particular group. In this case we have to use several relations with through
in a single model:
<span>class</span> Group <span>extends</span><span> CActiveRecord { </span>... <span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'roles'=><span>array</span>(self::HAS_MANY,'Role','group_id'), 'users'=><span>array</span><span>( self</span>::HAS_MANY,'User',<span>array</span>('user_id'=>'id'),'through'=>'roles'<span> )</span>, 'comments'=><span>array</span><span>( self</span>::HAS_MANY,'Comment',<span>array</span>('id'=>'user_id'),'through'=>'users'<span> )</span>,<span> ); } }</span>
<span>//</span><span> get all groups with all corresponding users</span> <span>$groups</span>=Group::model()->with('users')-><span>findAll(); </span><span>//</span><span> get all groups with all corresponding users and roles</span> <span>$groups</span>=Group::model()->with('roles','users')-><span>findAll(); </span><span>//</span><span> get all users and roles where group ID is 1</span> <span>$group</span>=Group::model()->findByPk(1<span>); </span><span>$users</span>=<span>$group</span>-><span>users; </span><span>$roles</span>=<span>$group</span>-><span>roles; </span><span>//</span><span> get all comments where group ID is 1</span> <span>$group</span>=Group::model()->findByPk(1<span>); </span><span>$comments</span>=<span>$group</span>->comments;
HAS_ONE
throughHAS_ONE through ER
An example of using HAS_ONE
with through
is getting user address where user is bound to address using profile. All these entities (user, profile, and address) do have corresponding models:
<span>class</span> User <span>extends</span><span> CActiveRecord { </span>... <span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'profile'=><span>array</span>(self::HAS_ONE,'Profile','user_id'), 'address'=><span>array</span><span>( self</span>::HAS_ONE,'Address',<span>array</span>('id'=>'profile_id'), 'through'=>'profile'<span> )</span>,<span> ); } }</span>
<span>//</span><span> get address of a user whose ID is 1</span> <span>$user</span>=User::model()->findByPk(1<span>); </span><span>$address</span>=<span>$user</span>->address;
through
can be used for a model bound to itself using a bridge model. In our case it's a user mentoring other users:
through self ER
That's how we can define relations for this case:
<span>class</span> User <span>extends</span><span> CActiveRecord { </span>... <span>public</span> <span>function</span><span> relations() { </span><span>return</span> <span>array</span><span>( </span>'mentorships'=><span>array</span><span>( self</span>::HAS_MANY,'Mentorship','teacher_id','joinType'=>'INNER JOIN'<span> )</span>, 'students'=><span>array</span><span>( self</span>::HAS_MANY,'User',<span>array</span>('student_id'=>'id'), 'through'=>'mentorships','joinType'=>'INNER JOIN'<span> )</span>,<span> ); } }</span>
<span>//</span><span> get all students taught by teacher whose ID is 1</span> <span>$teacher</span>=User::model()->findByPk(1<span>); </span><span>$students</span>=<span>$teacher</span>->students;
1. Yii
Yii is a component-based, high-performance PHP framework for developing large-scale Web applications. Yii is written in strict OOP and has complete library references and comprehensive tutorials. From MVC, DAO/ActiveRecord, widgets, caching, hierarchical RBAC, Web services, to principalization, I18N and L10N, Yii provides almost everything needed for today's Web 2.0 application development. And the price of this frame is not too high. In fact, Yii is one of the most efficient PHP frameworks.
2. CodeIgniter
CodeIgniter is an application development framework - a toolkit designed for people who build PHP websites. Its goal is to develop projects quickly: it provides a rich set of libraries to complete common tasks, as well as a simple interface and a well-organized structure to access these libraries. Developing with CodeIgniter can inject more creativity into your projects because it saves a lot of coding time.
3. CakePHP
CakePHP is a framework for rapid development of PHP, which uses some common design patterns such as ActiveRecord, Association Data Mapping, Front Controller and MVC. Its main goal is to provide a framework that enables PHP developers of any level to quickly develop web applications, and this rapid implementation does not sacrifice the flexibility of the project.
4. PHPDevShell
PHPDevShell is an open source (GNU/LGPL) rapid application development framework for developing pure PHP without Javascript. It has a complete GUI administrator backend interface. Its main goal is to develop management-based applications such as plug-ins, where speed, security, stability and flexibility are the top priorities. It's designed to have an easy learning curve, so PHP developers don't need to learn complex new terminology. The arrival of PHPDevShell meets developers' needs for a lightweight but fully functional GUI that can be configured without restrictions.
5. Akelos
Akelos PHP framework is a web application development platform based on the MVC design pattern. Based on good usage habits, you can use it to complete the following tasks:
◆Conveniently use Ajax to write views
◆Manage requests and responses through controllers
◆Manage internationalized applications
◆Use simple protocols to communicate with models and databases
Your Akelos application can run on most shared hosting providers because Akelos is server-friendly The only requirement is support for PHP. Therefore, the Akelos PHP framework is ideal for publishing stand-alone web applications because it does not require non-standard PHP configuration to run.
6. Symfony
Symfony is a web application framework for developing PHP5 projects.
The purpose of this framework is to speed up the development and maintenance of web applications and reduce repetitive coding work.
Symfony has low system requirements and can be easily installed on any setup: all you need is a Unix or Windows server with PHP5 installed on it. It is compatible with almost all databases. The price of Symfony is not high, much lower than the cost of a host.
For PHP developers, using Symfony is a natural thing, and the learning curve is only one day. Clean design and code readability will shorten development time. Developers can apply the principles of agile development (such as DRY, KISS or XP, etc.) and focus on the application logic level without spending a lot of time writing endless XML configuration files.
Symfony is designed to build complete enterprise-level applications. That is, you have control over the entire setup: from path structure to external libraries, almost everything can be customized. In order to comply with enterprise development regulations, Symfony also bundles some additional tools to facilitate project testing, debugging, and archiving.
7. Prado
The PRADO team is made up of some PRADO fanatics... The rest of the story>>
Let me give you a few keywords
1.MVC
2.Event-driven
3.ActiveRecord
4.Component-oriented
Don’t let these go, look carefully