SlideShare une entreprise Scribd logo
1  sur  28
Télécharger pour lire hors ligne
Advanced Querying with
CakePHP 3
1 / 28
Agenda
1. A short story
2. The philosophy behind the new ORM
3. The ORM goals
4. Simple Querying
5. Using SQL Functions
6. Subqueries
7. Working with associations
8. Associations Strategies
9. Filtering by Associations
10. Raw expressions and Deep Associations
11. Formatting your results
12. Intelligent Counters
2 / 28
A Short Story
Once upon a time there was a framework that worked like...
$this->Post->recursive = 3;
$this->Post->find('all');
And there was much rejoice, and then much sadness.
3 / 28
The Philosophy Behind the
New ORM
Understanding the ideas that brought us here will help you use effectively the ORM,
and enjoy using databases again.
4 / 28
The R in ORM
In 2010 I wrote a CakePHP datasource plugin for using MongoDB.
I thought it was cool.
b0ss: Can you please get last workshop attendees' emails?
me: Wait a minute, I need to write a custom Javascript program to get that data
It took much longer than a minute.
Relational databases won't go away anytime soon
They are extremely efficient
SQL is declarative language, even non programmers can get good at it
Allow you to query your data in any angle
5 / 28
No intentions of being
more than an ORM
This means:
Not going to connect to stuff that is not a relational database
We can focus on getting the most of of relational features
No incomplete abstractions
Does not mean:
That CakePHP 3 can't use NoSQL storage.
That you will spend hours scratching your head trying to wrap an API around a
fixed Interface.
6 / 28
Goals we had in mind
7 / 28
Clean layers
Each part of the subsystem should be usable on its own.
// OMG I'm not even using the ORM
$connection->newQuery()->select('*')->from('users');
// Ok, now I am
$table = TableRegistry::get('users');
$table->find();
8 / 28
Extensible column types
system
Do you really need ENUM? Go for it!
Type::map('enum', 'EnumType');
$users->schema()->columnType('role', 'enum');
9 / 28
Lazy (almost) all the things.
No database connections unless necessary.
No Queries executed unless needed.
// Look ma', no database connection have been made yet!
$users->find('all')->where(['username' => 'jose_zap']);
// Query was executed, nothing in memory yet
$users->find('all')->where(['username' => 'jose_zap'])->all();
// Only keep one row in memory at a time
$users->find('all')->where(['username' => 'jose_zap'])->bufferResults(false);
10 / 28
Should be fun to work with
Everything can be an expression
$query = $users->find()->select(['id'])->where(['is_active' => true]);
$anotherQuery->from(['stuff' => $query]);
$anotherQuery->innerJoin(['stuff' => $query]);
$anotherQuery->where(['id IN' => $query]);
Queries can be composed
$premium = $users->find('active')->find('premium')->each(function($user) {
echo $user->name;
});
$subscribers = $users->find('active')->find('subscribedToNewsletter');
$recipients = $premium->append($free)->extract('email');
11 / 28
The Setup
class CountriesTable extends Table {
public function initialize(array $config) {
$this->table('countries');
$this->belongsTo('Capitals', [
'foreignKey' => 'capital_id',
]);
$this->hasMany('Cities', [
'foreignKey' => 'country_id',
]);
$this->hasMany('Languages', [
'foreignKey' => 'country_id',
]);
}
12 / 28
Simple Querying
Monarchies with the largest population
public function findBiggestMonarchies(Query $query) {
return $query
->where(['government_form LIKE' => '%Monarchy%'])
->order(['population' => 'DESC']);
}
{
"name": "Japan",
"population": 126714000
},
{
"name": "Thailand",
"population": 61399000
},
{
"name": "United Kingdom",
"population": 59623400
},
13 / 28
Simple Querying
Republics in the world
public function findRepublics(Query $query) {
return $query
->where(['government_form' => 'Republic'])
->orWhere(['government_form' => 'Federal Republic']);
}
14 / 28
SQL Functions
Average life expectancy
public function findAverageLifeExpectancy(Query $query) {
return $query->select(['average_exp' => $query->func()->avg('life_expectancy')]);
}
{
"average_exp": 66.48604
}
15 / 28
Subqueries
public function findWithHighLifeExp(Query $query) {
$average = $this->find('findAverageLifeExpectancy');
return $query
->where(['life_expectancy >' => $average])
->order(['life_expectancy' => 'DESC']);
}
$countries->find('republics')->find('withHighLifeExp');
Republics with high life expectancy:
{
"name": "San Marino",
"life_expectancy": 81.1
},
{
"name": "Singapore",
"life_expectancy": 80.1
},
{
"name": "Iceland",
"life_expectancy": 79.4
}
16 / 28
Working with associations
$this->hasOne('OfficialLanguages', [
'className' => LanguagesTable::class,
'foreignKey' => 'country_id',
'conditions' => ['OfficialLanguages.is_official' => 'T']
]);
Official Languages
public function findWithOfficialLanguage(Query $query) {
return $query
->contain('OfficialLanguages');
}
17 / 28
Association strategies
public function findWithSpokenLanguages(Query $query, $options = []) {
if (!empty($options['languageStrategy'])) {
$this->Languages->strategy($options['languageStrategy']);
}
return $query
->contain('Languages');
}
Change the strategy:
$countries->find('withSpokenLanguages', ['languageStrategy' => 'subquery'])
And expect this SQL to be used:
SELECT * FROM languages AS Languages
WHERE country_id IN (SELECT id FROM countries AS Countries)
18 / 28
Filtering by associations
Cities with a population larger than
Denmark
public function findWithCitiesBiggerThanDenmark(Query $query) {
$denmarkPopulation = $this->find()
->select(['population'])
->where(['id' => 'DNK']);
return $query
->distinct(['Countries.id'])
->matching('Cities', function($q) use ($denmarkPopulation) {
return $q->where(['Cities.population >' => $denmarkPopulation]);
});
}
19 / 28
Raw SQL and Deep Assocs
I want to learn a new language, so I need to go to a city where that language is
spoken by at least 25% of the people who live there:
public function findCityProbability(Query $query) {
return $query
->matching('Countries.Cities', function($q) {
$prob = $q->newExpr(
'(Languages.percentage / 100) *' .
'(Cities.population / Countries.population)'
);
return $q
->select(['probability' => $prob, 'Cities.name'])
->where(function($exp) use ($prob) {
return $exp->gte($prob, 0.25);
});
});
}
20 / 28
Post processing
Things to keep in mind
Custom finders are required to return a Query object
Returning an array or a single value is not a Query
Therefore, you cannot return arrays or any other value
The Solution
Use formatResults()
Use mapReduce()
Use any of the Collection class methods after calling find()
21 / 28
Grouping by a Property
public function findInContinentGroups(Query $query) {
$query->formatResults(function($results) {
return $results->groupBy('continent');
});
return $query;
}
"Africa": [
{
"name": "Angola"
},
{
"name": "Burundi"
},
{
"name": "Benin"
},
{
"name": "Burkina Faso"
}
"America": [...
22 / 28
Getting Key - Value Lists
public function findOfficialLanguageList(Query $query) {
$query->formatResults(function($results) {
return $results->combine('name', 'official_language.language');
});
return $query->find('withOfficialLanguage');
}
{
"Aruba": "Dutch",
"Afghanistan": "Pashto",
"Albania": "Albaniana",
"Andorra": "Catalan",
"Netherlands Antilles": "Papiamento",
"United Arab Emirates": "Arabic",
"Argentina": "Spanish",
"Armenia": "Armenian",
...
23 / 28
Multiple Formatters
public function findInRegionalGroups(Query $query) {
$query
->formatResults(function($results) {
return $results->groupBy('continent');
})
->formatResults(function($results) {
return $results->map(function($continent) {
return collection($continent)->groupBy('region');
});
});
return $query;
}
"North America": {
"Caribbean": [
{
"name": "Aruba"
},
{
"name": "Anguilla"
},
{
"name": "Netherlands Antilles"
}
...
24 / 28
Intelligent Counts
$countries->find()
->select(function($query) {
return [
'average_life_expectancy' => $query->func()->avg('life_expectancy'),
'continent'
});
->group(['continent'])
->count(); // 7
Produces the following SQL:
SELECT COUNT(*) AS `count`
FROM (
SELECT (AVG(life_expectancy)), Countries.continent
FROM countries AS Countries GROUP BY continent
)
AS count_source
Pagination: piece of cake!
25 / 28
I have 99 problems...
Custom counting ain't one
Don't care about actual results counting in a pagination query?
Prefer using estimates or a different logic?
Use custom counters!
$query = $youtubeVideos->find('superComplexStuff')->counter(function() {
return Cache::read('estimated_results');
});
$query->count(); // 10000000
26 / 28
There's Plenty More!
But unfortunately, little time...
Result streaming
Query caching
Finder callbacks
Composite Primary Key searches
Methods for finding in Tree structures
27 / 28
Thanks for your time
Questions?
https://github.com/lorenzo/cakephp3-examples
28 / 28

Contenu connexe

Tendances

Decorators in Python
Decorators in PythonDecorators in Python
Decorators in Python
Ben James
 
애플리케이션 아키텍처와 객체지향
애플리케이션 아키텍처와 객체지향 애플리케이션 아키텍처와 객체지향
애플리케이션 아키텍처와 객체지향
Young-Ho Cho
 

Tendances (20)

Functional programming
Functional programmingFunctional programming
Functional programming
 
OPcache の最適化器の今
OPcache の最適化器の今OPcache の最適化器の今
OPcache の最適化器の今
 
Quill vs Slick Smackdown
Quill vs Slick SmackdownQuill vs Slick Smackdown
Quill vs Slick Smackdown
 
Decorators in Python
Decorators in PythonDecorators in Python
Decorators in Python
 
PHP 語法基礎與物件導向
PHP 語法基礎與物件導向PHP 語法基礎與物件導向
PHP 語法基礎與物件導向
 
C++17 std::filesystem - Overview
C++17 std::filesystem - OverviewC++17 std::filesystem - Overview
C++17 std::filesystem - Overview
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
 
애플리케이션 아키텍처와 객체지향
애플리케이션 아키텍처와 객체지향 애플리케이션 아키텍처와 객체지향
애플리케이션 아키텍처와 객체지향
 
Postgre sql best_practices
Postgre sql best_practicesPostgre sql best_practices
Postgre sql best_practices
 
Oracle APEX Cheat Sheet
Oracle APEX Cheat SheetOracle APEX Cheat Sheet
Oracle APEX Cheat Sheet
 
Monadic Java
Monadic JavaMonadic Java
Monadic Java
 
LaravelConf Taiwan 2017 單頁面應用與前後端分離開發
LaravelConf Taiwan 2017 單頁面應用與前後端分離開發LaravelConf Taiwan 2017 單頁面應用與前後端分離開發
LaravelConf Taiwan 2017 單頁面應用與前後端分離開發
 
iOS Automation: XCUITest + Gherkin
iOS Automation: XCUITest + GherkiniOS Automation: XCUITest + Gherkin
iOS Automation: XCUITest + Gherkin
 
Java SE 8 best practices
Java SE 8 best practicesJava SE 8 best practices
Java SE 8 best practices
 
Introduction to kotlin coroutines
Introduction to kotlin coroutinesIntroduction to kotlin coroutines
Introduction to kotlin coroutines
 
Chain of Responsibility Pattern
Chain of Responsibility PatternChain of Responsibility Pattern
Chain of Responsibility Pattern
 
Py.test
Py.testPy.test
Py.test
 
Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)Domain Modeling Made Functional (KanDDDinsky 2019)
Domain Modeling Made Functional (KanDDDinsky 2019)
 
Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)
 

Similaire à Advanced Querying with CakePHP 3

PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
elliando dias
 
Auto-loading of Drupal CCK Nodes
Auto-loading of Drupal CCK NodesAuto-loading of Drupal CCK Nodes
Auto-loading of Drupal CCK Nodes
nihiliad
 
ZendCon2010 The Doctrine Project
ZendCon2010 The Doctrine ProjectZendCon2010 The Doctrine Project
ZendCon2010 The Doctrine Project
Jonathan Wage
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
jsmith92
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
elliando dias
 

Similaire à Advanced Querying with CakePHP 3 (20)

Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolve
 
2013 - Benjamin Eberlei - Doctrine 2
2013 - Benjamin Eberlei - Doctrine 22013 - Benjamin Eberlei - Doctrine 2
2013 - Benjamin Eberlei - Doctrine 2
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Modularity and Layered Data Model
Modularity and Layered Data ModelModularity and Layered Data Model
Modularity and Layered Data Model
 
Auto-loading of Drupal CCK Nodes
Auto-loading of Drupal CCK NodesAuto-loading of Drupal CCK Nodes
Auto-loading of Drupal CCK Nodes
 
ZendCon2010 The Doctrine Project
ZendCon2010 The Doctrine ProjectZendCon2010 The Doctrine Project
ZendCon2010 The Doctrine Project
 
Easy rest service using PHP reflection api
Easy rest service using PHP reflection apiEasy rest service using PHP reflection api
Easy rest service using PHP reflection api
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applications
 
Agile database access with CakePHP 3
Agile database access with CakePHP 3Agile database access with CakePHP 3
Agile database access with CakePHP 3
 
How to write not breakable unit tests
How to write not breakable unit testsHow to write not breakable unit tests
How to write not breakable unit tests
 
Digital Mayflower - Data Pilgrimage with the Drupal Migrate Module
Digital Mayflower - Data Pilgrimage with the Drupal Migrate ModuleDigital Mayflower - Data Pilgrimage with the Drupal Migrate Module
Digital Mayflower - Data Pilgrimage with the Drupal Migrate Module
 
Backbone js
Backbone jsBackbone js
Backbone js
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
 
Durian: a PHP 5.5 microframework with generator-style middleware
Durian: a PHP 5.5 microframework with generator-style middlewareDurian: a PHP 5.5 microframework with generator-style middleware
Durian: a PHP 5.5 microframework with generator-style middleware
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
Practical PHP 5.3
Practical PHP 5.3Practical PHP 5.3
Practical PHP 5.3
 
PHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodePHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better Code
 
RESTful web services
RESTful web servicesRESTful web services
RESTful web services
 

Plus de José Lorenzo Rodríguez Urdaneta (6)

Faster develoment with CakePHP 3
Faster develoment with CakePHP 3Faster develoment with CakePHP 3
Faster develoment with CakePHP 3
 
CakeFest 2013 keynote
CakeFest 2013 keynoteCakeFest 2013 keynote
CakeFest 2013 keynote
 
CakePHP 3.0: Embracing the future
CakePHP 3.0: Embracing the futureCakePHP 3.0: Embracing the future
CakePHP 3.0: Embracing the future
 
ZeroMQ in PHP
ZeroMQ in PHPZeroMQ in PHP
ZeroMQ in PHP
 
Making the most out of CakePHP 2.2
Making the most out of CakePHP 2.2Making the most out of CakePHP 2.2
Making the most out of CakePHP 2.2
 
Mongo Cake Plugin for CakePHP 2.0
Mongo Cake Plugin for CakePHP 2.0Mongo Cake Plugin for CakePHP 2.0
Mongo Cake Plugin for CakePHP 2.0
 

Dernier

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Dernier (20)

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 

Advanced Querying with CakePHP 3

  • 2. Agenda 1. A short story 2. The philosophy behind the new ORM 3. The ORM goals 4. Simple Querying 5. Using SQL Functions 6. Subqueries 7. Working with associations 8. Associations Strategies 9. Filtering by Associations 10. Raw expressions and Deep Associations 11. Formatting your results 12. Intelligent Counters 2 / 28
  • 3. A Short Story Once upon a time there was a framework that worked like... $this->Post->recursive = 3; $this->Post->find('all'); And there was much rejoice, and then much sadness. 3 / 28
  • 4. The Philosophy Behind the New ORM Understanding the ideas that brought us here will help you use effectively the ORM, and enjoy using databases again. 4 / 28
  • 5. The R in ORM In 2010 I wrote a CakePHP datasource plugin for using MongoDB. I thought it was cool. b0ss: Can you please get last workshop attendees' emails? me: Wait a minute, I need to write a custom Javascript program to get that data It took much longer than a minute. Relational databases won't go away anytime soon They are extremely efficient SQL is declarative language, even non programmers can get good at it Allow you to query your data in any angle 5 / 28
  • 6. No intentions of being more than an ORM This means: Not going to connect to stuff that is not a relational database We can focus on getting the most of of relational features No incomplete abstractions Does not mean: That CakePHP 3 can't use NoSQL storage. That you will spend hours scratching your head trying to wrap an API around a fixed Interface. 6 / 28
  • 7. Goals we had in mind 7 / 28
  • 8. Clean layers Each part of the subsystem should be usable on its own. // OMG I'm not even using the ORM $connection->newQuery()->select('*')->from('users'); // Ok, now I am $table = TableRegistry::get('users'); $table->find(); 8 / 28
  • 9. Extensible column types system Do you really need ENUM? Go for it! Type::map('enum', 'EnumType'); $users->schema()->columnType('role', 'enum'); 9 / 28
  • 10. Lazy (almost) all the things. No database connections unless necessary. No Queries executed unless needed. // Look ma', no database connection have been made yet! $users->find('all')->where(['username' => 'jose_zap']); // Query was executed, nothing in memory yet $users->find('all')->where(['username' => 'jose_zap'])->all(); // Only keep one row in memory at a time $users->find('all')->where(['username' => 'jose_zap'])->bufferResults(false); 10 / 28
  • 11. Should be fun to work with Everything can be an expression $query = $users->find()->select(['id'])->where(['is_active' => true]); $anotherQuery->from(['stuff' => $query]); $anotherQuery->innerJoin(['stuff' => $query]); $anotherQuery->where(['id IN' => $query]); Queries can be composed $premium = $users->find('active')->find('premium')->each(function($user) { echo $user->name; }); $subscribers = $users->find('active')->find('subscribedToNewsletter'); $recipients = $premium->append($free)->extract('email'); 11 / 28
  • 12. The Setup class CountriesTable extends Table { public function initialize(array $config) { $this->table('countries'); $this->belongsTo('Capitals', [ 'foreignKey' => 'capital_id', ]); $this->hasMany('Cities', [ 'foreignKey' => 'country_id', ]); $this->hasMany('Languages', [ 'foreignKey' => 'country_id', ]); } 12 / 28
  • 13. Simple Querying Monarchies with the largest population public function findBiggestMonarchies(Query $query) { return $query ->where(['government_form LIKE' => '%Monarchy%']) ->order(['population' => 'DESC']); } { "name": "Japan", "population": 126714000 }, { "name": "Thailand", "population": 61399000 }, { "name": "United Kingdom", "population": 59623400 }, 13 / 28
  • 14. Simple Querying Republics in the world public function findRepublics(Query $query) { return $query ->where(['government_form' => 'Republic']) ->orWhere(['government_form' => 'Federal Republic']); } 14 / 28
  • 15. SQL Functions Average life expectancy public function findAverageLifeExpectancy(Query $query) { return $query->select(['average_exp' => $query->func()->avg('life_expectancy')]); } { "average_exp": 66.48604 } 15 / 28
  • 16. Subqueries public function findWithHighLifeExp(Query $query) { $average = $this->find('findAverageLifeExpectancy'); return $query ->where(['life_expectancy >' => $average]) ->order(['life_expectancy' => 'DESC']); } $countries->find('republics')->find('withHighLifeExp'); Republics with high life expectancy: { "name": "San Marino", "life_expectancy": 81.1 }, { "name": "Singapore", "life_expectancy": 80.1 }, { "name": "Iceland", "life_expectancy": 79.4 } 16 / 28
  • 17. Working with associations $this->hasOne('OfficialLanguages', [ 'className' => LanguagesTable::class, 'foreignKey' => 'country_id', 'conditions' => ['OfficialLanguages.is_official' => 'T'] ]); Official Languages public function findWithOfficialLanguage(Query $query) { return $query ->contain('OfficialLanguages'); } 17 / 28
  • 18. Association strategies public function findWithSpokenLanguages(Query $query, $options = []) { if (!empty($options['languageStrategy'])) { $this->Languages->strategy($options['languageStrategy']); } return $query ->contain('Languages'); } Change the strategy: $countries->find('withSpokenLanguages', ['languageStrategy' => 'subquery']) And expect this SQL to be used: SELECT * FROM languages AS Languages WHERE country_id IN (SELECT id FROM countries AS Countries) 18 / 28
  • 19. Filtering by associations Cities with a population larger than Denmark public function findWithCitiesBiggerThanDenmark(Query $query) { $denmarkPopulation = $this->find() ->select(['population']) ->where(['id' => 'DNK']); return $query ->distinct(['Countries.id']) ->matching('Cities', function($q) use ($denmarkPopulation) { return $q->where(['Cities.population >' => $denmarkPopulation]); }); } 19 / 28
  • 20. Raw SQL and Deep Assocs I want to learn a new language, so I need to go to a city where that language is spoken by at least 25% of the people who live there: public function findCityProbability(Query $query) { return $query ->matching('Countries.Cities', function($q) { $prob = $q->newExpr( '(Languages.percentage / 100) *' . '(Cities.population / Countries.population)' ); return $q ->select(['probability' => $prob, 'Cities.name']) ->where(function($exp) use ($prob) { return $exp->gte($prob, 0.25); }); }); } 20 / 28
  • 21. Post processing Things to keep in mind Custom finders are required to return a Query object Returning an array or a single value is not a Query Therefore, you cannot return arrays or any other value The Solution Use formatResults() Use mapReduce() Use any of the Collection class methods after calling find() 21 / 28
  • 22. Grouping by a Property public function findInContinentGroups(Query $query) { $query->formatResults(function($results) { return $results->groupBy('continent'); }); return $query; } "Africa": [ { "name": "Angola" }, { "name": "Burundi" }, { "name": "Benin" }, { "name": "Burkina Faso" } "America": [... 22 / 28
  • 23. Getting Key - Value Lists public function findOfficialLanguageList(Query $query) { $query->formatResults(function($results) { return $results->combine('name', 'official_language.language'); }); return $query->find('withOfficialLanguage'); } { "Aruba": "Dutch", "Afghanistan": "Pashto", "Albania": "Albaniana", "Andorra": "Catalan", "Netherlands Antilles": "Papiamento", "United Arab Emirates": "Arabic", "Argentina": "Spanish", "Armenia": "Armenian", ... 23 / 28
  • 24. Multiple Formatters public function findInRegionalGroups(Query $query) { $query ->formatResults(function($results) { return $results->groupBy('continent'); }) ->formatResults(function($results) { return $results->map(function($continent) { return collection($continent)->groupBy('region'); }); }); return $query; } "North America": { "Caribbean": [ { "name": "Aruba" }, { "name": "Anguilla" }, { "name": "Netherlands Antilles" } ... 24 / 28
  • 25. Intelligent Counts $countries->find() ->select(function($query) { return [ 'average_life_expectancy' => $query->func()->avg('life_expectancy'), 'continent' }); ->group(['continent']) ->count(); // 7 Produces the following SQL: SELECT COUNT(*) AS `count` FROM ( SELECT (AVG(life_expectancy)), Countries.continent FROM countries AS Countries GROUP BY continent ) AS count_source Pagination: piece of cake! 25 / 28
  • 26. I have 99 problems... Custom counting ain't one Don't care about actual results counting in a pagination query? Prefer using estimates or a different logic? Use custom counters! $query = $youtubeVideos->find('superComplexStuff')->counter(function() { return Cache::read('estimated_results'); }); $query->count(); // 10000000 26 / 28
  • 27. There's Plenty More! But unfortunately, little time... Result streaming Query caching Finder callbacks Composite Primary Key searches Methods for finding in Tree structures 27 / 28
  • 28. Thanks for your time Questions? https://github.com/lorenzo/cakephp3-examples 28 / 28