Code Monkey home page Code Monkey logo

cakephp's Introduction

CakePHP

Software License Coverage Status PHPStan Code Consistency Total Downloads Latest Stable Version

CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Associative Data Mapping, Front Controller, and MVC. Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility.

Installing CakePHP via Composer

You can install CakePHP into your project using Composer. If you're starting a new project, we recommend using the app skeleton as a starting point. For existing applications you can run the following:

composer require cakephp/cakephp

For details on the (minimum/maximum) PHP version see version map.

Running Tests

Assuming you have PHPUnit installed system wide using one of the methods stated here, you can run the tests for CakePHP by doing the following:

  1. Copy phpunit.xml.dist to phpunit.xml.
  2. Add the relevant database credentials to your phpunit.xml if you want to run tests against a non-SQLite datasource.
  3. Run phpunit.

Some Handy Links

  • CakePHP - The rapid development PHP framework.
  • CookBook - The CakePHP user documentation; start learning here!
  • API - A reference to CakePHP's classes.
  • Awesome CakePHP - A list of featured resources around the framework.
  • Plugins - A repository of extensions to the framework.
  • The Bakery - Tips, tutorials and articles.
  • Community Center - A source for everything community related.
  • Training - Join a live session and get skilled with the framework.
  • CakeFest - Don't miss our annual CakePHP conference.
  • Cake Software Foundation - Promoting development related to CakePHP.

Get Support!

  • Slack - Join us on Slack.
  • Discord - Join us on Discord.
  • #cakephp on irc.freenode.net - Come chat with us, we have cake.
  • Forum - Official CakePHP forum.
  • GitHub Issues - Got issues? Please tell us!
  • Roadmaps - Want to contribute? Get involved!

Contributing

Security

If you’ve found a security issue in CakePHP, please use the procedure described in SECURITY.md.

cakephp's People

Contributors

ad7six avatar admad avatar antograssiot avatar bar avatar bcrowe avatar ceeram avatar chinpei215 avatar dakota avatar felixge avatar garas avatar havokinspiration avatar jadb avatar jeremyharris avatar jperras avatar jrbasso avatar lordsimal avatar lorenzo avatar markstory avatar nateabele avatar ndm2 avatar othercorey avatar phpnut avatar predominant avatar ravage84 avatar rchavik avatar renan avatar robertpustulka avatar saeideng avatar shama avatar stickler-ci avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cakephp's Issues

Translation with recursives request

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #32):


(Reported by: akira)

Hello community ^^

So, I've a little problem with a multi-languages website. I've a page with many linked table using recursive mode in level 2 or more. And I use too the translation system in database with the table i18n.

The system work very well for simple model using one table to be translated. But if the table is in second or next level in recursive mod, the system doesn't work to translate automatically the elements of table.

Now, I just have this problem for one of my pages and fixe it manually. But if my website grow, I think it will be a really problem when many tables will be linked.

I come from french community and I'm not alone with this problem.

If you want more information tell me, you have my mail address

afterFind() and beforeFind() callbacks not working on associated models [was: Translate Behavior should translate associated model data]

Created by Predominant, 6th Mar 2013. (originally Lighthouse ticket #95):


(Reported by: thinline)

What Happened

Using the Blog example if you have Posts which belogsTo Categories and Category actsAs Translate. Within the Posts controller calling $this->Post->find() or read() does not translate the Category relationship. This happens if the Category model is loaded.
See https://trac.cakephp.org/ticket/3680

I am only filing this here because I can't not find a search feature in the new code.cakephp.org and would like to make sure this gets into the to-do-list

mssql database error while primary key contains space in name

Created by arek, 17th Jul 2013. (originally Lighthouse ticket #484):


On Microsoft SQL database fields can cantains empty char "space" in fieldname. When this occurs with field which is primary key then we can't save new record. It happens because part of the name after space is taken as operator part in DboSource->__parseKey method. And when we would like to create new record with given primary key value cake, before saving does checking if such record exists and is generating bad query to do this.

I'm using 1.2.6 version of cakephp.

Add include/exlcue model id option for FormHelper

Created by Jesh, 14th Jun 2011. (originally Lighthouse ticket #583):


In 1.3 FormHelper appends editing item's id to form action URL if $this->data contain the working model primary key field. However sometimes i don't want the id be appended but currently there are no ways to disable it. For example when a authenticated user editing his own profile, I like the address nice view as '/account/profile' instead of '/account/profile/123' which 123 is the current user's id. I known my problem can be easily addressed by unset $this->data['User']['id'] in my case, but i prefer i can control it in view.

Maybe in FormHelper::create(), we can set options['url']['id'] = false and do some checking so $id will remain false and not appended. Hope below snippet can help.

Changing line 228

$id = $this->data[$model][$data['key']];

to

if(!isset($options['url'][$data['key']]) || $options['url'][$data['key']]!==false) {
   $id = $this->data[$model][$data['key']];
}

Thanks

Adding empty option to radio form helper

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #3):


(Reported by: thinline)

Using the Form Helper to add empty radio field

  • When using the form helper to create a radio box I would like to have a empty filed exposed. I know this seems a little counter intuitive but it would function the same as if no option was selected. The value of this is to allow people to de-select a radio option and or leave the option blank.
  • Used the following code:
  • <?=$form->input('Qualifiers.Categories', array('type' => 'radio', 'empty' => 'Select None'))?>

I am putting this out as a RFC to see if there is a compelling reason that this should not be included. If not then I will submit a patch to the form helper.

$model->unBindModel() should use Set::normalize()

Created by Gabriel Totoliciu, 3rd May 2010. (originally Lighthouse ticket #660):


When writing
$this->unBindModel(array("hasAndBelongsToMany"=>"SomeModel"));
a warning occurs:
Warning (2): Invalid argument supplied for foreach() [CORE/cake/libs/model/model.php, line 581]

I know that the correct form would be
$this->unBindModel(array("hasAndBelongsToMany"=>array("SomeModel")));

But if we write
$models=Set::normalize($models, true);
before
foreach ($models as $model) { ([CORE/cake/libs/model/model.php, line 581])

Then both forms would work ok.

In BeforeSave you can't change id (primaryKey)

Created by riotera, 7th Apr 2011. (originally Lighthouse ticket #678):


In my behavior I check through the callback method beforeSave() if the record exists and in this case I set the id for data to be saved (updated).
It doesn't work because in model.php the save method check if the record exists before the beforeSave()

$exists = $this->exists();

and after I assign the id it is set to false because

if (!$exists && $count > 0) {
    $this->id = false;
}

It generates an INSERT in place of an UPDATE query as I would like.

PATCH: Media View supporting ranges (= iPhone videos)

Created by Martin Westin, 21st Jan 2010. (originally Lighthouse ticket #246):


MediaView does not support ranges correctly in 1.2.5. This is a problem for anyone wanting to output video to iphone from a Cake app since iPhone requires support for ranges since OS 3.1.2.

Problem 1 is that MediaView only checks for ranges for "forced downloads" and not for "normal" output.
Problem 2 is that the code for ranges that does exist has a few bugs in it which makes use of ranges fail.

I include a temporary patch for the above problems. The code in its current form should only be used if you urgently require ranges support in your app. There are a number of problems, missing checks and ugly messes that I still need to clean up. I still wanted to get this out asap so that the cleaning-up can be done with some feedback behind it.

Missing is:
validating the input from the headers more fully.
supporting multiple ranges in the same request (crazy but it is part of the spec)
returning correct error headers for bad ranges

  • a lot of cleaning up and refactoring to remove duplicate code.

I will post another patch when I have ha a chance to do some more work on this.

allowEmpty shortcuts multiple validations on same field

Created by Tim Jones, 10th Feb 2010. (originally Lighthouse ticket #328):


It appears that allowEmpty => true causes Cake to shortcut out of the validation sequence for fields with multiple validations when the input is empty. While I can see that, in most cases, this behavior makes sense there are some in which it does not and it's not clear, or documented as far as I can tell, that this behavior is in place. In my case I wanted to allow a field to be empty in most cases, except when a certain condition was met. I chose to address this by first using a validation rule with 'allowEmpty' => true and a second custom validation rule that potentially reversed the 'allowEmpty' logic under certain conditions.

As an example:

class MyModel extends AppModel {
$validates = array('my_field' => array('rule1' => array('rule' => array('custom', '/some_regexp/'),
'required' => false,
'allowEmpty' => true),
'rule2' => array('rule' => 'my_custom_validation')))

function my_custom_validation($field) {
    $values = array_values($field);
    if(some_condition_met && empty($values[0]) {
        return false;
    }
    return true;
}

}

My expectation was that rule1 would run followed by rule2 for an empty value for 'my_field'. This isn't the case however and Cake breaks out of the validation on line 2432 of model.php (v 1.2.5) after running rule1 and finding the allowEmpty requirement with an empty field. Although I think the behavior won't do any harm in 90% of cases I don't think it makes sense in all of them. It violates the principle of least surprise and could, at the very least, use some documentation. Note that reversing the order of the rules solves the problem.

Containable behavior does not implement 'group' option

Created by doughsay, 13th Apr 2010. (originally Lighthouse ticket #579):


The containable behavior seems to implement most of the options that a regular find can do, but leaves out 'group'.

I can do something like this just fine:

$this->Model->find('all', array( 'contain' => array( 'AssociatedModel' => array( 'fields' => array('field1', 'field2'), 'order' => array('field1'), 'conditions' => array('field2' => 'foo'), ) ) ));

But how about something like this?

$this->Model->find('all', array( 'contain' => array( 'AssociatedModel' => array( 'fields' => array('foreign_key_id', 'SUM(field)'), 'group' => array('foreign_key_id') ) ) ));

I would expect this to add "GROUP BY foreign_key_id" to the query, as it does in find, but instead it thinks that 'group' is a field on AssociatedModel, and causes an SQL error.

Extending core library classes

Created by Jamie, 4th Apr 2010. (originally Lighthouse ticket #540):


I've tagged this as RFC because I'd like to get the core dev team's input on adding support for extending core library classes. Just as there are Model -> AppModel, Controller -> AppController, etc. inheritance chains, why shouldn't we have the ability to extend/overwrite any core library class?

For example, if I want to add some custom functionality to the Router class, I'd like to be able to do it without editing the core. If all of the core library classes - Router, Set, File, Folder, etc. - were renamed with Cake prefixes (some of them are already), then the core functionality could be retained in CakeRouter, CakeSet, CakeFile (and so on), with room for extension in empty classes that would come with the core (Router extends CakeRouter, Set extends CakeSet, etc.).

When I read about 1.3's new app/libs support, I had originally thought that its purpose was for extending core classes. Apparently I was wrong. ;) Don't get me wrong, I like the first-party library support and use it quite often, but I would also love to add functionality to the core while still being able to update painlessly to the newest version.

Some tables don't have primaryKeys, and that's okay...

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #5):


(Reported by: mikesmullin)

Sometimes you just want to define a model like:

class MyModelName extends AppModel {
   var $primaryKey = FALSE;
}

Now, truly, in our case our table really does have a primaryKey--but it is a combination of two or more fields, and CakePHP currently does not appear to support this. So this is the next-best solution.

I have a patch. I will see if I can attach it.

cakephp should respect precision and scale on numeric column types with test fixtures

Created by Seth Cleveland, 13th Apr 2010. (originally Lighthouse ticket #577):


When writing unit tests using imported fixtures, cakephp should respect a numeric columns precision and scale. This enables people to write better test cases using the import feature with CakeTestFixtures. The following diff is applied to cakephp 1.2.6 to enable setting the precision and scale with numeric columns. These changes were tested against oracle 10g.

Index: cake/libs/model/datasources/dbo/dbo_oracle.php
===================================================================
--- cake/libs/model/datasources/dbo/dbo_oracle.php  (revision 120)
+++ cake/libs/model/datasources/dbo/dbo_oracle.php  (working copy)
@@ -479,7 +479,7 @@
            return $cache;
        }

-       $sql = 'SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM all_tab_columns WHERE table_name = \'';
+       $sql = 'SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE FROM all_tab_columns WHERE table_name = \'';
        $sql .= strtoupper($this->fullTableName($model)) . '\'';

        if (!$this->execute($sql)) {
@@ -491,7 +491,9 @@
        for ($i = 0; $row = $this->fetchRow(); $i++) {
            $fields[strtolower($row[0]['COLUMN_NAME'])] = array(
                'type'=> $this->column($row[0]['DATA_TYPE']),
-               'length'=> $row[0]['DATA_LENGTH']
+               'length'=> $row[0]['DATA_LENGTH'],
+               'precision'=> $row[0]['DATA_PRECISION'],
+               'scale'=> $row[0]['DATA_SCALE'],
            );
        }
        $this->__cacheDescription($this->fullTableName($model, false), $fields);
@@ -1118,4 +1120,4 @@
            return $out;
        }
 }
Index: cake/libs/model/datasources/dbo_source.php
===================================================================
--- cake/libs/model/datasources/dbo_source.php  (revision 120)
+++ cake/libs/model/datasources/dbo_source.php  (working copy)
@@ -2361,7 +2361,12 @@
        $out = $this->name($name) . ' ' . $real['name'];

        if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
-           if (isset($column['length'])) {
+           if ($column[ 'precision'] != '') {
+               $length = $column[ 'precision'];
+               if ($column[ 'scale'] != '') {
+                   $length .= ',' . $column[ 'scale'];
+               } 
+           } else if (isset($column['length'])) {
                $length = $column['length'];
            } elseif (isset($column['limit'])) {
                $length = $column['limit'];
``` php

PaginateCount optimization

Created by Andras Kende, 9th Mar 2010. (originally Lighthouse ticket #445):


Hello,

I was wondering if the PaginateCount could be optimized to not do any left joins if the paginate conditions is not containing any associated models.

This would speed up the PaginateCount queries on larger tables as it could do count without unnecessary joins...

The same logic could go to CounterCache as well..

For example right now I do it manually with passing an extra paramter to the model PaginateCount function as:

Controller:

$this->paginate = array(
'extra' => array('recursive' => -1),
'limit' => 50,
'recursive' => 0,
'order' => array('Topic.modified' => 'DESC'),
'conditions' => array('Topic.active' => 1),
'fields' => array(
'Topic.id',
'Topic.name',
'User.name',
'Category.name'
)
);
$this->set('topics', $this->paginate('Topic'));
}

Model:

public function PaginateCount($conditions = null, $recursive = 0, $extra = array()) {

$rec = empty($extra['extra']['recursive']) ? $recursive : $extra['extra']['recursive'];

return $this->find('count', array(
  'conditions' => $conditions,
  'recursive' => $rec,
));

}

Thank you,
Andras

Validation of HABTM Data Needed

Created by Jamie, 27th Feb 2010. (originally Lighthouse ticket #400):


It seems that validating HABTM data is not possible using the $validates array of the main model. For example, if I'm working with model User which HABTM Group, and I want to ensure that every User is associated with at least one Group, I'd logically want to put a rule in the User::$validates array, such as:

class User extends AppModel {
    var $validate = array(
        'Group' => array(
            'rule' => array('multiple', array('min' => 1)),
            'required' => true,
            'message' => 'Please select at least one group for this user.'
        )
    );
}

Unfortunately, I can't do that, since HABTM data never even gets passed to the validator, since in the Model::invalidFields() function, which handles data validation, it's stripped:

$data = $this->data;

if (isset($data[$this->alias])) {
    $data = $data[$this->alias];
} elseif (!is_array($data)) {
    $data = array();
}

I know that saving the User will call the Group model validation, so I can just have a rule there, but (in my opinion) that goes against the spirit of data validation, since we're not validating the Group itself, just the relationship User has to Group. This HABTM validation should take place in the main model, User. A Group model record is not being saved, so we shouldn't need to rely on the Group $validates array to validate the HABTM User->Group relationship.

It's quite possible that I'm missing something, but the code in Model::invalidFields() is pretty cut and dry. I don't see how validating the presence of a HABTM relationship from the main model's $validate array is possible, when it really should be without any hacks or workarounds.

TranslateBehaviour should check if locale is already set in I18n

Created by NaMB, 4th Jan 2010. (originally Lighthouse ticket #165):


I suggest to change this

    function _getLocale(&$model) {
        if (!isset($model->locale) || is_null($model->locale)) {
            if (!class_exists('I18n')) {
                App::import('Core', 'i18n');
            }
            $I18n =& I18n::getInstance();
            $I18n->l10n->get(Configure::read('Config.language'));
            $model->locale = $I18n->l10n->locale;
        }

        return $model->locale;
    }

to this :

            $I18n =& I18n::getInstance();
            if (!$I18n->l10n->found)
                $I18n->l10n->get(Configure::read('Config.language'));

[TEST] Cross database joins with conditions fail

Created by Kim Biesbjerg, 24th Mar 2010. (originally Lighthouse ticket #508):


When doing cross database joins using conditions the generated SQL is invalid.

TEST:
function testCrossDatabaseJoinsWithConditions() {
$config = new DATABASE_CONFIG();

    $skip = $this->skipIf(
        !isset($config->test) || !isset($config->test2),
         '%s Primary and secondary test databases not configured, skipping cross-database '
        .'join tests.'
        .' To run these tests, you must define $test and $test2 in your database configuration.'
    );

    if ($skip) {
        return;
    }

    $this->loadFixtures('Article', 'User');
    $TestModel =& new Article();

    $expected = array(
        0 => array(
            'Article' =>  array(
                'id' => '3',
                'user_id' => '1',
                'title' => 'Third Article',
                'body' => 'Third Article Body',
                'published' => 'Y',
                'created' => '2007-03-18 10:43:23',
                'updated' => '2007-03-18 10:45:31'
            ),
            'User' => array(
                'id' => '1',
                'user' => 'mariano',
                'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
                'created' => '2007-03-17 01:16:23',
                'updated' => '2007-03-17 01:18:31'
            ),
            'Comment' => array(
            ),
            'Tag' => array(
            )
        )
    );

    $TestModel->User->setDataSource('test2');
    $results = $TestModel->find('all', array(
        'conditions' => array(
            'Article.id' => 3,
            'User.user' => 'mariano' 
        )
    ));
    $this->assertEqual($results, $expected);
}

DATABASE_CONFIG class:

class DATABASE_CONFIG {

    var $default = array(
        'driver' => 'mysql',
        'persistent' => false,
        'host' => 'localhost',
        'login' => 'user',
        'password' => 'password',
        'database' => 'database_name',
        'prefix' => '',
    );

    var $test = array(
        'driver' => 'mysql',
        'persistent' => false,
        'host' => 'localhost',
        'login' => 'root',
        'password' => '12ecb4',
        'database' => 'cake_test',
        'prefix' => '',
    );

    var $test2 = array(
        'driver' => 'mysql',
        'persistent' => false,
        'host' => 'localhost',
        'login' => 'root',
        'password' => '12ecb4',
        'database' => 'cake_test2',
        'prefix' => '',
    );
}

Allow joined tables conditions in find() condition

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #25):


(Reported by: deadyman)

This is just a little enhancement to avoid changing hasMany['Foo']['conditions'] or bindModel using when you have to change joining conditions punctually.

Old

Foo {
   var $hasMany=array('Bar'=>array('conditions'=>array('active'=>1)), 'Other'=>array(...));
   var $belongsTo = array('Table');
}

Foo->bindModel(array('hasMany'=>array('Bar'=>array('active'=>0), 'Other'=>Table->hasMany['Other'])));
Foo->find('all', array('conditions'=>array('Table.field'=>'bla')));

Idea

Foo {
   var $hasMany=array('Bar'=>array('conditions'=>array('active'=>1), 'Other'=>array(...)));
   var $belongsTo = array('Table');
}

Foo->find('all', array('conditions'=>array('Table.field'=>'bla', 'Bar.active'=>0)));

Obviously, set Bar conditions cancel $hasMany conditions only on 'Bar' joining.

[rfc] bake validation questions

Created by dogmatic69, 9th Mar 2010. (originally Lighthouse ticket #444):


say you have a table with 20 or 30 fields, and you just want to validate the first 2 or 3, name, slug, description for example, and the rest are modified, updated, a bunch of flags, and related_id fields

it would be cool for the end of the validation list to have a [q] option to break out the loop of fields in the tables and continue the bake.

saveAll does not return error is related model failed to save

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #26):


(Reported by: rostislav)

What happened:

Model ProfileCriteria with Position in hasAndBelongsToMany. Calling saveAll for data with both models. Returned result is:

Array
(
    [ProfileCriteria] => 1
)

While there is a database FK constrain failure: Warning (512): SQL Error: 1452: Cannot add or update a child row: a foreign key constraint fails....

DboSource::showQuery() - CORE\cake\libs\model\datasources\dbo_source.php, line 514
DboSource::execute() - CORE\cake\libs\model\datasources\dbo_source.php, line 201
DboSource::create() - CORE\cake\libs\model\datasources\dbo_source.php, line 574
Model::save() - CORE\cake\libs\model\model.php, line 1223
AppModel::save() - APP\models\app_model.php, line 54
Model::__saveMulti() - CORE\cake\libs\model\model.php, line 1328
Model::save() - CORE\cake\libs\model\model.php, line 1236
AppModel::save() - APP\models\app_model.php, line 54
Model::__save() - CORE\cake\libs\model\model.php, line 1624
Model::saveAll() - CORE\cake\libs\model\model.php, line 1528
ProfileCriteriasController::save() - APP\controllers\profile_criterias_controller.php, line 500
Object::dispatchMethod() - CORE\cake\libs\object.php, line 115
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 245
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 211
Object::requestAction() - CORE\cake\libs\object.php, line 100
EuresController::integrate() - APP\controllers\eures_controller.php, line 373
Object::dispatchMethod() - CORE\cake\libs\object.php, line 117

What was expected:

Return result:

Array
(
    [ProfileCriteria] => 0
)

Can't modify related model's data in beforeSave when using saveAll

Created by kennethfrey (at gmail), 5th May 2010. (originally Lighthouse ticket #671):


Post hasMany Comment. Comment belongsTo Post.

$this->data = array(
'Post' => array(
'title' => 'mytitle',
'text' => 'mytext')
'Comment' => array(
0 => array('text' => 'mytext'),
1 => array('text' => 'mytext')
)
)

I am trying to use saveAll($this->data, array('validate' => 'first')) to save a Post and a multiple Comments. I am trying to modify $this->data['Comment'][0]['text'] to a different value. I can set it and debug just before returning true in beforeSave and the data looks correct.

I remove the debug let it save and it doesn't get set. I can change the data from the Comment model beforeSave but then the rest of $this->data isn't available and I need the other information in order to change the value how I want it. Is this a bug or feature?

Lazy and Conditional Issue of BEGIN/COMMIT/ROLLBACK

Created by Stephen Cuppett, 1st Apr 2010. (originally Lighthouse ticket #530):


It's easy enough to code yourself into nested functions, conditionals, etc. that end up doing something like this (sql log):

12 begin 0 0 0
13 commit 0 0 0
14 begin 0 0 0
15 commit 0 0 0
16 begin 0 0 0
17 commit 0 0 0
18 begin 0 0 0
19 commit 0 0 0
20 begin 0 0 0
21 commit 0 0 2
22 begin 0 0 0
23 commit 0 0 0
24 begin 0 0 0
25 commit 0 0 0
26 begin 0 0 0
27 commit 0 0 0
28 begin 0 0 0
29 commit 0 0 0
30 begin 0 0 0
31 commit 0 0 0
32 begin 0 0 0
33 commit 0 0 0
34 begin 0 0 0
35 commit 0 0 0
36 begin 0 0 0
37 commit 0 0 0
38 begin 0 0 0
39 commit 0 0 0
40 begin 0 0 0
41 commit 0 0 0
42 begin 0 0 0
43 commit 0 0 0
44 begin 0 0 0
45 commit

Until the first SELECT/INSERT/UPDATE/DELETE (or custom query) needs to hit the database, BEGIN could not be issued (put on the wire chewing up time) and unless an INSERT/UPDATE/DELETE (or custom query) has been issued, COMMIT/ROLLBACK is not necessary either...

[rfc] bake all by table prefix

Created by dogmatic69, 8th Mar 2010. (originally Lighthouse ticket #435):


bake model all is cool, but say you have 100 tables and creating a new plugin with about 20 tables.

i normally have the plugin name as the prefix and when wanting to bake this the options currently are cake bake model all and remove the other models, or doing it 1 by 1.

was thinking of having a new param for bake all, something like

cake bake model all -plugin something -prefix something_

and that would just bake the models with tables that start with something_

'exclusive' setting on hasMany rfc/problems in Model::_deleteDependent()

Created by Dieter Plaetinck, 6th Apr 2010. (originally Lighthouse ticket #549):


Hi,
http://book.cakephp.org/view/1039/Associations-Linking-Models-Together#hasMany-1043
This is about the 'exclusive' setting for the hasmany model relation in cake1.3 but probably older versions too.

  1. The cookbook says that using 'exclusive', i.e. doing a deleteAll() "greatly improves performance, but may not be ideal for all circumstances. " Please explain, why is this not ideal for all circumstances?

  2. Non-exclusive (default behavior!) is vulnerable to race condititions.
    See http://github.com/cakephp/cakephp1x/blob/518cab91e58c9dc1c18d8d629e152b8a8903f852/cake/libs/model/model.php#L1852
    Records can be added/updated/deleted between the $model->find('all') call and the $model->delete(), which can cause breakage/data corruption (rows deleted which are reattached to something else, not enough rows deleted, rows not existing anymore, ..)

Model Invalidate does not support nested fields

Created by Chickenhen, 12th Jan 2010. (originally Lighthouse ticket #194):


if you have a model with data

$this->data = array('Article' => 
  array('Users' =>
    array('user1' => 'val1', 'user2' => 'val2', 'user3' => 'val3')
  ));

from within Article model, call

$this->Invalidate('Users.user1', 'error message');

the form helper will not pick up that the nested field user1 is invalidated.

i.e.

$this->Form->input('Article.Users.user1');

will not show the associated error message

can try messing with FormHelper::invalidate [sets validation error path], and Helper::tagIsInvalid [tries to extract validation error path]

ACL Non-nested parentNode()s

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #16):


(Reported by: mikebeanz)

What happened

  • After returning a list of nodes from Model::parentNode(), Cake does not use the list of nodes for verifying access control.

What was expected:

  • CakePHP should properly verify permissions from the requester inherited from each parentNode.
  • Conflicts should be resolved in one of three ways (application-wide setting?):
    • -1: Deny last
    • 0: Tree Order
    • 1: Allow last

Validation Suggestion

Created by ckdarby, 22nd Mar 2010. (originally Lighthouse ticket #498):


Adds 10x functionality to /libs/model/model.php; Allows validation used for everything, login form can use the validations but isn't going to get caught with the registration isUnique validation that I use for the registration form.

function validates($options = array(), $ignore = array()) {
    $errors = $this->invalidFields($options, $ignore);
    if (empty($errors) && $errors !== false) {
        $errors = $this->__validateWithModels($options);
    }
    if (is_array($errors)) {
        return count($errors) === 0;
    }
    return $errors;
}

function invalidFields($options = array(), $ignore = array()) {
    if (
        !$this->Behaviors->trigger(
            $this,
            'beforeValidate',
            array($options),
            array('break' => true, 'breakOn' => false)
        ) ||
        $this->beforeValidate($options) === false
    ) {
        return false;
    }

    if (!isset($this->validate) || empty($this->validate)) {
        return $this->validationErrors;
    }

    $data = $this->data;
    $methods = array_map('strtolower', get_class_methods($this));
    $behaviorMethods = array_keys($this->Behaviors->methods());

    if (isset($data[$this->alias])) {
        $data = $data[$this->alias];
    } elseif (!is_array($data)) {
        $data = array();
    }

    $Validation =& Validation::getInstance();
    $this->exists();

    $_validate = $this->validate;
    $whitelist = $this->whitelist;

    if (array_key_exists('fieldList', $options)) {
        $whitelist = $options['fieldList'];
    }

    if (!empty($whitelist)) {
        $validate = array();
        foreach ((array)$whitelist as $f) {
            if (!empty($this->validate[$f])) {
                $validate[$f] = $this->validate[$f];
            }
        }
        $this->validate = $validate;
    }

    foreach ($this->validate as $fieldName => $ruleSet) {
        if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) {
            $ruleSet = array($ruleSet);
        }
        $default = array(
            'allowEmpty' => null,
            'required' => null,
            'rule' => 'blank',
            'last' => false,
            'on' => null
        );

            foreach ($ruleSet as $index => $validator) {
            if(!in_array($index, $ignore)){
                if (!is_array($validator)) {
                    $validator = array('rule' => $validator);
                }
                $validator = array_merge($default, $validator);

                if (isset($validator['message'])) {
                    $message = $validator['message'];
                } else {
                    $message = __('This field cannot be left blank', true);
                }

                if (
                    empty($validator['on']) || ($validator['on'] == 'create' &&
                    !$this->__exists) || ($validator['on'] == 'update' && $this->__exists
                )) {
                    $required = (
                        (!isset($data[$fieldName]) && $validator['required'] === true) ||
                        (
                            isset($data[$fieldName]) && (empty($data[$fieldName]) &&
                            !is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false
                        )
                    );

                    if ($required) {
                        $this->invalidate($fieldName, $message);
                        if ($validator['last']) {
                            break;
                        }
                    } elseif (array_key_exists($fieldName, $data)) {
                        if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) {
                            break;
                        }
                        if (is_array($validator['rule'])) {
                            $rule = $validator['rule'][0];
                            unset($validator['rule'][0]);
                            $ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule']));
                        } else {
                            $rule = $validator['rule'];
                            $ruleParams = array($data[$fieldName]);
                        }

                        $valid = true;

                        if (in_array(strtolower($rule), $methods)) {
                            $ruleParams[] = $validator;
                            $ruleParams[0] = array($fieldName => $ruleParams[0]);
                            $valid = $this->dispatchMethod($rule, $ruleParams);
                        } elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) {
                            $ruleParams[] = $validator;
                            $ruleParams[0] = array($fieldName => $ruleParams[0]);
                            $valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams);
                        } elseif (method_exists($Validation, $rule)) {
                            $valid = $Validation->dispatchMethod($rule, $ruleParams);
                        } elseif (!is_array($validator['rule'])) {
                            $valid = preg_match($rule, $data[$fieldName]);
                        }

                        if (!$valid || (is_string($valid) && strlen($valid) > 0)) {
                            if (is_string($valid) && strlen($valid) > 0) {
                                $validator['message'] = $valid;
                            } elseif (!isset($validator['message'])) {
                                if (is_string($index)) {
                                    $validator['message'] = $index;
                                } elseif (is_numeric($index) && count($ruleSet) > 1) {
                                    $validator['message'] = $index + 1;
                                } else {
                                    $validator['message'] = $message;
                                }
                            }
                            $this->invalidate($fieldName, $validator['message']);

                            if ($validator['last']) {
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    $this->validate = $_validate;
    return $this->validationErrors;
}

REST methods PUT and DELETE defaulting to index()

Created by bravo_kernel, 9th Mar 2010. (originally Lighthouse ticket #440):


Description: REST webservice not returning a 404 error on missing funtions for PUT and DELETE methods but instead returning the index()/GET function.

Setup:

  • Cake 1.3 (latest Git) REST webservice based on the example in the book (http://book.cakephp.org/view/1239/The-Simple-Setup):

    Router::mapResources('recipes');

    Router::parseExtensions();

    The RecipesController has only an index() function and that function works as expected.

Tests run against the service with a REST-client show the following:

  • GET: works as expected => returns a list of Recipes as defined in index()
  • POST: works as expected => returns a 404 error (since there is no delete() function in the controller)
  • PUT: does not work NOT as expected => defaults to index() and returns a list of Recipes when a 404 is expected
  • DELETE:does not work NOT as expected => defaults to index() and returns a list of Recipes when a 404 is expected

Discussed on IRC extensively with Predominant.

DboSource::_matchRecords() race conditions and usage re affectedrows

Created by Dieter Plaetinck, 16th Mar 2010. (originally Lighthouse ticket #468):


DboSource::_matchRecords() is used in 2 places:
DboSource::update() and DboSource::delete()

In both places it's used to check if records can be found matching the specified conditions before doing the actual update/delete.

However:

  1. this is vulnerable to race conditions. Matching rows can appear or vanish between the find('all') call in _matchRecords() and the actual query execution in DboSource::update() or DboSource::delete().
  2. (imho) it's bad practice anyway to return false in DboSource::update() and DboSource::delete() when there will be/was no affected row. "false" is a return value which should only be used in case of errors. This also how it's documented in the api:
    http://api.cakephp.org/class/dbo-source#method-DboSourceupdate
    http://api.cakephp.org/class/dbo-source#method-DboSourcedelete

I propose to get rid of the redundant find('all') calls, just execute the update/delete queries. And (only) in case of failure, return false.
If users want a reliable way to check if rows were affected, they can always use Model::getAffectedRows()

If you guys agree with this logic, I will write a patch for 1.3

postgres support for serializable transactions instead of just read committed

Created by Dieter Plaetinck, 6th Apr 2010. (originally Lighthouse ticket #548):


Postgres supports 2 kinds of transactions, see
http://www.postgresql.org/docs/8.4/interactive/mvcc.html

  • read committed (default on 'BEGIN')
  • serializable

There currently is no proper way to do serializable transaction queries on postgres with cakephp.
There are two ways to make a transaction serializable:

  • 'BEGIN; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'
  • 'START TRANSACTION ISOLATION LEVEL SERIALIZABLE'

see
http://www.postgresql.org/docs/7.4/interactive/sql-set-transaction.html
http://www.postgresql.org/docs/8.4/interactive/sql-start-transaction.html

In an application, you can just do

$Model->query('START TRANSACTION ISOLATION LEVEL SERIALIZABLE');

but it would be nice if cake had support out of the box for this.
I would submit a patch myself, but I'm not sure how i should implement this. any pointers?
But the fix may be more trivial then the explanation, so feel free :)

Dieter

No unsigned columns in test suite tables

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #17):


(Reported by: mensler)

What happened:

I created a fixture for the following table with public $import = 'IpToCountry';:

CREATE TABLE `ip_to_countries` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `num_ip_from` int(10) unsigned NOT NULL,
  `num_ip_to` int(10) unsigned NOT NULL,
  `ip_from` varchar(15) NOT NULL,
  `ip_to` varchar(15) NOT NULL,
  `country_id` char(2) NOT NULL,
  `country_name` varchar(128) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `num_ip_from_num_ip_to` (`num_ip_from`,`num_ip_to`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

The test suite creates the table without the unsigned flag, so large numbers like 3741057024 become 2147483647 on insert.

Form helper inconsistencies

Created by ProLoser, 15th Apr 2010. (originally Lighthouse ticket #587):


This currently will display a multiple select box:

$this->Form->input('HabtmModel');

If you pass the following you get a set of checkboxes without fieldset/legend:

$this->Form->input('HabtmModel', array('multiple' => 'checkbox'));

If you pass the following you will get a set of radios with a fieldset/legend:

$this->Form->input('HabtmModel', array('type' => 'radio'));

My question is twofold:

  1. Is it necessary to use multiple => checkbox AND type => radio/checkbox? Couldn't we just use 'type' and detect and utilize the existing array?
  2. It would be easier (and more logical) to group an array-generated set of checkboxes together using a fieldset and legend just like radios do now.

virtualFields in 1st result row not being mapped

Created by FF_Dan, 7th Apr 2010. (originally Lighthouse ticket #554):


I've only seen this behaviour on oracle so far, so not sure if other dbo's are affected;

The code in dbo_source.php (ln ~403):

$first = $this->fetchRow();
if ($first != null) {
    $out[] = $first;
}
while ($this->hasResult() && $item = $this->fetchResult()) {
    $this->fetchVirtualField($item);
    $out[] = $item;
}

If $first is not null it is assigned to $out without first calling fetchVirtualField()

paginate and custom find failing count

Created by franiglesias, 30th Dec 2010. (originally Lighthouse ticket #182):


Trying to use controller->paginate() with some custom model find methods that use joins, I've got the following error: Warning (512): SQL Error: 1054: Unknown column 'Channel.slug' in 'where clause'.

I guess that the error comes from the model->_findCount method called by controller->paginate(). This method should honor the custom find, but it doesn't and constructs the query using only the data in the controller->paginate property for the model being paginated.

So, I suggest to change the model->_findCount method to build the count query starting form the generated by the find method passed in the options. A possible patch is attached.

B_Tree Behavior for Bigger, Faster Trees

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #10):


(Reported by: busytoby)

The standard TreeBehavior has a flaw causing add/delete based performance degredation bound by the size of the table. In a table of 1 million nodes a single add or delete can currently cause up to 2 million discreet operations. To reduce the effect of this degradation I propose introducing fragmentation and partitioning to the Tree table, thereby significantly reducing the overhead of destructive or additive operations.

The core behavior treats each $left and $right value as a contiguous point on a line from 0->N where N = MAX($right). Any destructive or additive operation involving a node X must currently modify every $left and $right value in the entire table greater than X($left). The time required to perform such operations is therefore directly bound by MAX($right) if we want to retain any semblance of consistency in the tree. Performance of recover() and reorder() are thus bound to factorial degradation as the size of the tree grows.

The solution I propose is to introduce partitioning and fragmentation in the table.

In this case, we allocate each new tree enough space for $block_size = 32k nodes. Since $left and $right are bound by MAX_INT this effectively gives us enough space for MAX_INT/(2*$block_size) nodes. In the default scenario this yields space for 2^32/(2*2^15) = 2^16 or 64k possible trees.

The benefit to this partitioning is that our operations previously requiring O(N) time bound by the size of the table now require O(log(N)) time bound by the size of the individual subtree of NULL being modified. This has the potential to bring the time for a recover() or reorder() operation down from potentially weeks to a number of hours. The chance for data corruption is likewise reduced due to the significant overall reduction of modifications to the table.

So what does it break? :).. not much hopefully. Aside from continuity between $right,$left nodes of children of NULL the code should be 100% data-compatible. My hope is that the BTree is a drop-in replacement that will automatically adapt an existing Tree to its structure over time but verify() will certainly fail the continuity check if a BTree table is likewise used with the Tree behavior.

The 'Upgrade':

  • change $actsAs = array(..., Tree) to $actsAs = array(..., BTree)
    you can stop here and as you delete/create trees they will be auto-partitioned as needed.

Optionally perform 1 of the following to partition your existing trees.

  • export and re-import all trees in the table. This will be faster than recover() on most large tables
  • execute recover() on the inherited model

lastly, run verify() to ensure the data set is clean.

Schema update doesn't change length of boolean columns

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #61):


(Reported by: hlidotbe)

What happened

When updating a schema containing boolean columns, if the old column is already a tinyint but of different length, the new column inherit this length. In addition to having a wrong updated schema, every time update is ran, boolean columns are in the diff since they are not of the correct length.

What was expected

Length should be removed or set to the limit (which is of lower priority than all other length source, i.e. column length, column limit, and real length).

For my needs I've taken the "remove" approach, which seems to have the lowest impact. Below is the diff against 1.2.5:

diff -r 4f9a7d8cfcc4 libs/model/schema.php
--- a/libs/model/schema.php Tue Nov 10 17:51:38 2009 +0100
+++ b/libs/model/schema.php Thu Nov 12 15:37:57 2009 +0100
@@ -411,7 +411,7 @@
                if (isset($old[$table][$field])) {
                    $diff = array_diff_assoc($value, $old[$table][$field]);
                    if (!empty($diff) && $field !== 'indexes') {
-                       $tables[$table]['change'][$field] = array_merge($old[$table][$field], $diff);
+                       $tables[$table]['change'][$field] = array_intersect_key(array_merge($old[$table][$field], $diff), $value);
                    }
                }

_saveMulti() with non numeric HABTM join key

Created by pluriels, 11th Mar 2010. (originally Lighthouse ticket #448):


Using a database with string primary key, _multiSave doesn't expect data to be strings.

line 1423 model.php
For a string, this test returns true

$row = 'string';
isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])

The fix is to test if the value is an array
if not, data provided are strings

} elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']]) && is_array($row)) {
  $newData[] = $row;
} else {
  $newValues[] = "('{$id}', '{$row}')";
}

Filtering paginated results with conditions on I18n fields

Created by Jpsy, 5th Apr 2010. (originally Lighthouse ticket #545):


I am anything but an experienced CakePHP nerd, so please forgive me, if this is (again) a misconception of myself. But here is, what I think to be an issue in 1.3.0 RC3:

Given:
MyModel with I18n activated on some fields
MyController with pagination

Task:
Use conditions in $this->paginate() to filter the find results.

Problem:
The PaginationHelper issues two finds:

  1. find('count') to count the pages and
  2. find('all') to collect the data.
    These two finds use completely different alias names for the I18n joins:
  3. something like I18nModel (without preselected I18n fields
  4. something like I18n__title (where a field is selected)
    Now, if I set a condition in $this->paginate() this condition is injected into both finds. Because the aliases do not match, my condition will either fail in 1. or in 2. with an Unknown Column error - I can never write a condition that wont throw an error.

Solution:
I solved the problem for the time being by a beforeFind() function in MyModel that checks for the existence of join alias I18nModel in the query. If this alias does not exist, a corresponding join is added to the query. So I can always rely on the existence of that alias.

function beforeFind($queryData){
$joinFound=false;
foreach($queryData['joins'] as $join){
if($join['alias']=='I18nModel'){
$joinFound=true; break;
}
}
if(!$joinFound){
$newJoin = array(
'type'=>'INNER',
'table'=>'i18n',
'alias'=>'I18nModel',
'conditions'=>array(
$this->name.'.id'=>Set::map(array(
'type'=>'identifier',
'value'=>'I18nModel.foreign_key',
)),
'I18nModel.model'=>$this->name,
'I18nModel.locale'=>$this->locale,
),
);
array_push($queryData['joins'], $newJoin);
}
return $queryData;
}

I think that something like this should be part of the CakePHP core, so conditions on I18n fields will work for pagination - or more general: the same conditions can be used for all finds on I18n fields.

Cheers, Jörg.

update sequence when inserting record with fixed id?

Created by Dieter Plaetinck, 15th Apr 2010. (originally Lighthouse ticket #584):


when using sequences on a database that supports it (eg postgresql) (or serial columnt type).
if you:

  • add a new record with a specific id, eg '1' (such as is generally recommended for fixtures, see http://book.cakephp.org/view/1201/Preparing-test-data). the record gets inserted (assuming it validates and does not violate constraints) but the sequence nextval stays the same. by default nextval is 1 (also Cake's test suite does ALTER SEQUENCE .. RESTART WITH 1 queries)
  • want to insert a new record that has no id specified (this is what you usually do when doing $Model->save()), the primary key will we duplicate and violate the unique constraint, because it uses the nextval (which is 1)

I'm not sure what the recommended approach should be.

  • discourage specific 'id' setting, even when running tests? this makes things a bit harder, but not problematic.
  • automatically update the sequence nextval when 'id' is specified? maybe as an optional setting for models?
  • make it the applications job to enhance the model (maybe with a behavior) to update the sequence?

[PATCH] Using CakeTestCase::$dropTables = false;

Created by klevo, 17th Dec 2009. (originally Lighthouse ticket #119):


I use CakeTestCase::$dropTables = false; because the group tests run much faster thanks to not dropping/recreating tables between test cases. However 1.3 does not create the test tables at all if this var is set to false. Attached is a patch for cake_test_case.php.

PATCH: Tweak Translate to allow no I18n table Model

Created by Predominant, 9th Dec 2009. (originally Lighthouse ticket #22):


(Reported by: michaelc)

The translate behavior is great, and does many things that I like. One last thing I would ask regarding the field 'field'. As far as retrieving data is concerned, I am able to rename the field 'field' to 'name' or 'title', in order to ensure the display field is automatically chosen, and thereby not need a model for the table. (I have several translation tables due to separation of concerns, and it's a bother to have more model files). The diff below fixes that last case where Model::displayField is not just used instead of a hardcoded value. Thanks.

--- cake/libs/model/behaviors/translate.php     2009-10-15 17:49:04.000000000 +0100
+++ translate.php       2009-10-15 17:49:00.000000000 +0100
@@ -295,7 +295,7 @@

                foreach ($tempData as $field => $value) {
                        unset($conditions['content']);
-                       $conditions['field'] = $field;
+                       $conditions[$RuntimeModel->displayField] = $field;
                        if (is_array($value)) {
                                $conditions['locale'] = array_keys($value);
                        } else {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.