SlideShare a Scribd company logo
1 of 79
Download to read offline
HANDLING EXCEPTIONAL CONDITIONSHANDLING EXCEPTIONAL CONDITIONS
WITH GRACE AND STYLEWITH GRACE AND STYLE
Nikola Poša · @nikolaposa
Я радий бути тутЯ радий бути тут
Я радий бути тутЯ радий бути тут
ABOUT MEABOUT ME
Software Architect specializing in PHP-based
applications
Lead Architect at Arbor Education Partners
PHP Serbia Conference co-organizer
 @nikolaposa
 blog.nikolaposa.in.rs
AGENDAAGENDA
Approaches for dealing with exceptional
conditions
Set of applicable best practices for managing
exceptions in a proper way
Solution for establishing central error handling
system
Few tips for testing exceptions
HAPPY PATHHAPPY PATH
a.k.a. Normal Flow
Happy path is a default scenario
featuring no exceptional or error
conditions, and comprises nothing if
everything goes as expected.
Wikipedia
“
$user = $userRepository->get('John');
if ($user->isSubscribedTo($notification)) {
$notifier->notify($user, $notification);
}
1
2
3
4
5
$user = $userRepository->get('John');
if ($user->isSubscribedTo($notification)) {
$notifier->notify($user, $notification);
}
1
2
3
4
5
if ($user->isSubscribedTo($notification)) {
Fatal error: Call to a member function isSubscribedTo() on null
$user = $userRepository->get('John');1
2
3
$notifier->notify($user, $notification);4
}5
6
7
8
$user = $userRepository->get('John');
if ($user->isSubscribedTo($notification)) {
$notifier->notify($user, $notification);
}
1
2
3
4
5
$user = $userRepository->get('John');
if (null !== $user && $user->isSubscribedTo($notification)) {
$notifier->notify($user, $notification);
}
1
2
3
4
5
Null checks ood
if (null !== $user) {
if (null !== $todo) {
if (null !== $notification) {
$user = $userRepository->get('John');1
2
3
$todo = $todoRepository->get('Book flights');4
5
6
$notification = TodoReminder::from($todo, $user);7
8
9
if ($user->isSubscribedTo($notification)) {10
$notifier->notify($user, $notification);11
}12
}13
}14
}15
Vague Interface
interface UserRepository
{
public function get(string $username): ?User;
}
Vague Interface
interface UserRepository
{
public function get(string $username): ?User;
}
interface UserRepository
{
/**
* @param UserId $id
* @return User|bool User instance or boolean false if User w
*/
public function get(string $username);
}
DO NOT MESS WITHDO NOT MESS WITH
NULLNULL
When we return null, we are
essentially creating work for
ourselves and foisting problems upon
our callers.
Robert C. Martin, "Clean Code"
“
If you are tempted to return null
from a method, consider throwing
an exception or returning a
Special Case object instead.
Robert C. Martin, "Clean Code"
“
THROW EXCEPTIONTHROW EXCEPTION
interface UserRepository
* @throws UserNotFound
public function get(string $username): User;
1
{2
/**3
* @param string $username4
5
* @return User6
*/7
8
}9
throw new UserNotFound();
final class DbUserRepository implements UserRepository1
{2
public function get(string $username): User3
{4
$userRecord = $this->db->fetchAssoc('SELECT * FROM use5
6
if (false === $userRecord) {7
8
}9
10
return User::fromArray($userRecord);11
}12
}13
interface UserRepository
{
@throws UserNotFound
public function get(string $username): User;
}
try {
$user = $userRepository->get($username);
if ($user->isSubscribedTo($notification)) {
$notifier->notify($user, $notification);
}
} catch (UserNotFound $ex) {
$this->logger->notice('User was not found', ['username' => $u
}
try {
$user = $userRepository->get($username);
if ($user->isSubscribedTo($notification)) {
$notifier->notify($user, $notification);
}
} catch (UserNotFound $ex) {
$this->logger->notice('User was not found', ['username' => $u
}
try {
$this->notifyUserIfSubscribed($username, $notification);
} catch (Throwable $ex) {
$this->log($ex);
}
SPECIAL CASESPECIAL CASE
a.k.a. Null Object
A subclass that provides special
behavior for particular cases.
Martin Fowler, "Patterns of Enterprise Application
Architecture"
“
class UnknownUser extends User
{
public function username(): string
{
return 'unknown';
}
public function isSubscribedTo(Notification $notification): b
{
return false;
}
}
return new UnknownUser();
final class DbUserRepository implements UserRepository1
{2
public function get(string $username): User3
{4
$userRecord = $this->db->fetchAssoc('SELECT * FROM use5
6
if (false === $userRecord) {7
8
}9
10
return User::fromArray($userRecord);11
}12
}13
$user = $userRepository->get('John');
if ($user->isSubscribedTo($notification)) {
$notifier->notify($user, $notification);
}
Checking for Special Case
Checking for Special Case
if ($user instanceof UnknownUser) {
//do something
}
Checking for Special Case
if ($user instanceof UnknownUser) {
//do something
}
if ($user === User::unknown()) {
//do something
}
Special Case factory
class User
{
public static function unknown(): User
{
static $unknownUser = null;
if (null === $unknownUser) {
$unknownUser = new UnknownUser();
}
return $unknownUser;
}
}
Special Case object as private nested class
class User
{
public static function unknown(): User
{
static $unknownUser = null;
if (null === $unknownUser) {
$unknownUser = new class extends User {
public function username(): string
{
return 'unknown';
}
public function isSubscribedTo(Notification $noti
{
return false;
}
};
}
return $unknownUser;
}
}
Returning null from methods is
bad, but passing null into methods
is worse.
Robert C. Martin, "Clean Code"
“
class Order
{
public function __construct(
Product $product,
Customer $customer,
?Discount $discount
) {
$this->product = $product;
$this->customer = $customer;
$this->discount = $discount;
}
}
final class PremiumDiscount implements Discount
{
public function apply(float $productPrice): float
{
return $productPrice * 0.5;
}
}
?Discount $discount
if (null !== $this->discount) {
}
class Order1
{2
public function __construct(3
Product $product,4
Customer $customer,5
6
) {7
$this->product = $product;8
$this->customer = $customer;9
$this->discount = $discount;10
}11
12
public function total(): float13
{14
$price = $this->product->getPrice();15
16
17
$price = $this->discount->apply($price);18
19
20
return $price;21
}22
}23
Discount $discount
class Order1
{2
public function __construct(3
Product $product,4
Customer $customer,5
6
) {7
$this->product = $product;8
$this->customer = $customer;9
$this->discount = $discount;10
}11
}12
Discount $discount
class Order1
{2
public function __construct(3
Product $product,4
Customer $customer,5
6
) {7
$this->product = $product;8
$this->customer = $customer;9
$this->discount = $discount;10
}11
}12
final class NoDiscount implements Discount
{
public function apply(float $productPrice): float
{
return $productPrice;
}
}
$order = new Order($product, $customer, new NoDiscount());
EXCEPTION VS SPECIAL CASEEXCEPTION VS SPECIAL CASE
Special Case as default strategy instead of
optional parameters
Exceptions break normal ow to split business
logic from error handling
Special Case handles exceptional behaviour
Exception emphasizes violated business rule
USING EXCEPTIONSUSING EXCEPTIONS
Should be simple as:
throw new Exception('User was not found by username: ' . $usernam
Should be simple as:
throw new Exception('User was not found by username: ' . $usernam
CUSTOM EXCEPTION TYPESCUSTOM EXCEPTION TYPES
bring semantics to your code
emphasise exception type instead of exception
message
allow caller to act di erently based on
Exception type
STRUCTURING EXCEPTIONSSTRUCTURING EXCEPTIONS
src/
Todo/
Exception/
Model/
User/
Exception/
Model/
CREATING EXCEPTION CLASSESCREATING EXCEPTION CLASSES
src/
User/
Exception/
InvalidUsername.php
UsernameAlreadyTaken.php
UserNotFound.php
final class UserNotFound extends Exception
{
}
use AppUserExceptionUserNotFoundException;
try {
throw new UserNotFoundException();
} catch (UserNotFoundException $exception) {
}
use AppUserExceptionUserNotFoundException;
try {
throw new UserNotFoundException();
} catch (UserNotFoundException $exception) {
}
use AppUserExceptionUserNotFound;
try {
throw new UserNotFound();
} catch (UserNotFound $exception) {
}
COMPONENT LEVEL EXCEPTIONCOMPONENT LEVEL EXCEPTION
TYPETYPE
COMPONENT LEVEL EXCEPTIONCOMPONENT LEVEL EXCEPTION
TYPETYPE
namespace AppUserException;
interface ExceptionInterface
{
}
final class InvalidUsername extends Exception implements
ExceptionInterface
{
}
final class UserNotFound extends Exception implements
ExceptionInterface
{
}
use GuzzleHttpExceptionClientException;
use GuzzleHttpExceptionServerException;
use GuzzleHttpExceptionGuzzleException; //marker interface
try {
//code that can emit exceptions
} catch (ClientException $ex) {
//...
} catch (ServerException $ex) {
//...
} catch (GuzzleException $ex) {
//...
}
FORMATTING EXCEPTIONFORMATTING EXCEPTION
MESSAGESMESSAGES
FORMATTING EXCEPTIONFORMATTING EXCEPTION
MESSAGESMESSAGES
throw new UserNotFound(sprintf(
'User was not found by username: %s',
$username
));
FORMATTING EXCEPTIONFORMATTING EXCEPTION
MESSAGESMESSAGES
throw new UserNotFound(sprintf(
'User was not found by username: %s',
$username
));
throw new InsufficientPermissions(sprintf(
'You do not have permission to %s %s with the id: %s',
$privilege,
get_class($entity),
$entity->getId()
));
Encapsulate formatting into Exception classes
final class UserNotFound extends Exception implements ExceptionI
{
public static function byUsername(string $username): self
{
return new self(sprintf(
'User was not found by username: %s',
$username
));
}
}
Named Constructors communicate the intent
throw UserNotFound::byUsername($username);
Coherent exceptional conditions
throw TodoNotOpen::triedToSetDeadline($deadline, $this->status);
throw TodoNotOpen::triedToMarkAsCompleted($this->status);
PROVIDE CONTEXTPROVIDE CONTEXT
PROVIDE CONTEXTPROVIDE CONTEXT
final class UserNotFound extends Exception implements ExceptionI
{
private string $username;
public static function byUsername(string $username): self
{
$ex = new self(sprintf('User was not found by username: %
$ex->username = $username;
return $ex;
}
public function username(): string
{
return $this->username;
}
}
EXCEPTION WRAPPINGEXCEPTION WRAPPING
EXCEPTION WRAPPINGEXCEPTION WRAPPING
try {
return $this->toResult(
$this->httpClient->request('GET', '/users')
);
} catch (ConnectException $ex) {
throw ApiNotAvailable::reason($ex);
}
1
2
3
4
5
6
7
EXCEPTION WRAPPINGEXCEPTION WRAPPING
try {
return $this->toResult(
$this->httpClient->request('GET', '/users')
);
} catch (ConnectException $ex) {
throw ApiNotAvailable::reason($ex);
}
1
2
3
4
5
6
7
final class ApiNotAvailable extends Exception implements Exce
{
public static function reason(ConnectException $error): se
{
return new self(
'API is not available',
0,
$error //preserve previous error
);
}
}
1
2
3
4
5
6
7
8
9
10
11
EXCEPTION WRAPPINGEXCEPTION WRAPPING
try {
return $this->toResult(
$this->httpClient->request('GET', '/users')
);
} catch (ConnectException $ex) {
throw ApiNotAvailable::reason($ex);
}
1
2
3
4
5
6
7
final class ApiNotAvailable extends Exception implements Exce
{
public static function reason(ConnectException $error): se
{
return new self(
'API is not available',
0,
$error //preserve previous error
);
}
}
1
2
3
4
5
6
7
8
9
10
11
$error //preserve previous error
final class ApiNotAvailable extends Exception implements Exce1
{2
public static function reason(ConnectException $error): se3
{4
return new self(5
'API is not available',6
0,7
8
);9
}10
}11
RETROSPECTRETROSPECT
1. create custom, cohesive Exception types
2. introduce component-level exception type
3. use Named Constructors to encapsulate
message formatting and express the intent
4. capture & provide the context of the
exceptional condition
5. apply exception wrapping to rethrow more
informative exception
ERROR HANDLINGERROR HANDLING
WHEN TO CATCH EXCEPTIONS?WHEN TO CATCH EXCEPTIONS?
WHEN TO CATCH EXCEPTIONS?WHEN TO CATCH EXCEPTIONS?
Do NOT catch exceptions
unless you can handle the problem so that the
application continues to work
CENTRAL ERROR HANDLERCENTRAL ERROR HANDLER
Wraps the entire system to handle any uncaught
exceptions from a single place
CHALLENGESCHALLENGES
user experience
security
logging
CHALLENGESCHALLENGES
user experience
security
logging
adaptability
EXISTING SOLUTIONSEXISTING SOLUTIONS
EXISTING SOLUTIONSEXISTING SOLUTIONS
- stack-based error handling, pretty
error page, handlers for di erent response
formats (JSON, XML)
Whoops
EXISTING SOLUTIONSEXISTING SOLUTIONS
- stack-based error handling, pretty
error page, handlers for di erent response
formats (JSON, XML)
- di erent formatting strategies
(HTML, JSON, CLI), logging handler, non-
blocking errors
Whoops
BooBoo
Using Whoops
final class ErrorHandlerFactory
{
public function __invoke(ContainerInterface $container)
{
$whoops = new WhoopsRun();
if (WhoopsUtilMisc::isAjaxRequest()) {
$whoops->pushHandler(new JsonResponseHandler());
} elseif (WhoopsUtilMisc::isCommandLine()) {
$whoops->pushHandler(new CommandLineHandler());
} else {
$whoops->pushHandler(new PrettyPageHandler());
}
$whoops->pushHandler(new SetHttpStatusCodeHandler());
$whoops->pushHandler(new LogHandler($container->get('Logg
return $whoops;
}
}
src/bootstrap.php
public/index.php
bin/app
//... initialize DI container
$container->get(WhoopsRun::class)->register();
return $container;
$container = require __DIR__ . '/../src/bootstrap.php';
$container->get('AppWeb')->run();
$container = require __DIR__ . '/../src/bootstrap.php';
$container->get('AppConsole')->run();
Logging errors
final class LogHandler extends Handler
{
public function handle()
{
$error = $this->getException();
if ($error instanceof DontLog) {
return self::DONE;
}
$this->logger->error($error->getMessage(), [
'exception' => $error,
]);
return self::DONE;
}
}
final class UserNotFound extends Exception implements
ExceptionInterface,
DontLog
{
//...
}
Setting HTTP status code
final class SetHttpStatusCodeHandler extends Handler
{
public function handle()
{
$error = $this->getException();
$httpStatusCode =
($error instanceof ProvidesHttpStatusCode)
? $error->getHttpStatusCode()
: 500;
$this->getRun()->sendHttpCode($httpStatusCode);
return self::DONE;
}
}
interface ProvidesHttpStatusCode
{
public function getHttpStatusCode(): int;
}
final class UserNotFound extends Exception implements
ExceptionInterface,
DontLog,
ProvidesHttpStatusCode
{
//...
public function getHttpStatusCode(): int
{
return 404;
}
}
The OCP (Open­Closed Principle) is
one of the driving forces behind the
architecture of systems. The goal is
to make the system easy to extend
without incurring a high impact of
change.
Robert C. Martin, "Clean Architecture"
“
TEST EXCEPTIONALTEST EXCEPTIONAL
BEHAVIOURBEHAVIOUR
a.k.a. Negative Testing
TESTING EXCEPTIONS WITHTESTING EXCEPTIONS WITH
PHPUNITPHPUNIT
class TodoTest extends TestCase
{
/**
* @test
*/
public function it_throws_exception_on_reopening_if_incomp
{
$todo = Todo::from('Book flights', TodoStatus::OPEN())
$this->expectException(CannotReopenTodo::class);
$todo->reopen();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
TESTING EXCEPTIONS WITHTESTING EXCEPTIONS WITH
PHPUNITPHPUNIT
class TodoTest extends TestCase
{
/**
* @test
*/
public function it_throws_exception_on_reopening_if_incomp
{
$todo = Todo::from('Book flights', TodoStatus::OPEN())
$this->expectException(CannotReopenTodo::class);
$todo->reopen();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$this->expectException(CannotReopenTodo::class);
$todo->reopen();
class TodoTest extends TestCase1
{2
/**3
* @test4
*/5
public function it_throws_exception_on_reopening_if_incomp6
{7
$todo = Todo::from('Book flights', TodoStatus::OPEN())8
9
10
11
12
}13
}14
ARRANGE-ACT-ASSERTARRANGE-ACT-ASSERT
1. initialize SUT/prepare inputs
2. perform action
3. verify outcomes
class TodoTest extends TestCase
{
/**
* @test
*/
public function it_gets_completed()
{
$todo = Todo::from('Book flights', TodoStatus::OPEN());
$todo->complete();
$this->assertTrue($todo->isCompleted());
}
}
/**
* @test
*/
public function it_throws_exception_on_reopening_if_incomplete()
{
$todo = Todo::from('Book flights', TodoStatus::OPEN());
try {
$todo->reopen();
$this->fail('Exception should have been raised');
} catch (CannotReopenTodo $ex) {
$this->assertSame(
'Tried to reopen todo, but it is not completed.',
$ex->getMessage()
);
}
}
Thank youThank you
Drop me some feedback and make this
presentation better
·
joind.in/talk/8a8d6
@nikolaposa blog.nikolaposa.in.rs

More Related Content

What's hot

The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
Fabien Potencier
 

What's hot (20)

BEAR DI
BEAR DIBEAR DI
BEAR DI
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
 
Symfony2 - OSIDays 2010
Symfony2 - OSIDays 2010Symfony2 - OSIDays 2010
Symfony2 - OSIDays 2010
 
Coding website
Coding websiteCoding website
Coding website
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
Form API Intro
Form API IntroForm API Intro
Form API Intro
 
Александр Трищенко: PHP 7 Evolution
Александр Трищенко: PHP 7 EvolutionАлександр Трищенко: PHP 7 Evolution
Александр Трищенко: PHP 7 Evolution
 
Daily notes
Daily notesDaily notes
Daily notes
 
Top 10 php classic traps DPC 2020
Top 10 php classic traps DPC 2020Top 10 php classic traps DPC 2020
Top 10 php classic traps DPC 2020
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony application
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12
 
Symfony War Stories
Symfony War StoriesSymfony War Stories
Symfony War Stories
 
Drupal 8: Routing & More
Drupal 8: Routing & MoreDrupal 8: Routing & More
Drupal 8: Routing & More
 
Drupal 8: Forms
Drupal 8: FormsDrupal 8: Forms
Drupal 8: Forms
 
Advanced modulinos trial
Advanced modulinos trialAdvanced modulinos trial
Advanced modulinos trial
 
Php tour 2018 un autre regard sur la validation (1)
Php tour 2018   un autre regard sur la validation (1)Php tour 2018   un autre regard sur la validation (1)
Php tour 2018 un autre regard sur la validation (1)
 
logic321
logic321logic321
logic321
 

Similar to Nikola Poša "Handling exceptional conditions with grace and style"

Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)
Fabien Potencier
 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitinternational PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
smueller_sandsmedia
 

Similar to Nikola Poša "Handling exceptional conditions with grace and style" (20)

Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Mocking Demystified
Mocking DemystifiedMocking Demystified
Mocking Demystified
 
Using and reusing CakePHP plugins
Using and reusing CakePHP pluginsUsing and reusing CakePHP plugins
Using and reusing CakePHP plugins
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
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
 
Solid principles
Solid principlesSolid principles
Solid principles
 
The command dispatcher pattern
The command dispatcher patternThe command dispatcher pattern
The command dispatcher pattern
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitinternational PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
 
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
 
Dependency Injection and Pimple
Dependency Injection and PimpleDependency Injection and Pimple
Dependency Injection and Pimple
 
Demystifying Object-Oriented Programming - Lone Star PHP
Demystifying Object-Oriented Programming - Lone Star PHPDemystifying Object-Oriented Programming - Lone Star PHP
Demystifying Object-Oriented Programming - Lone Star PHP
 
Demystifying Object-Oriented Programming - ZendCon 2016
Demystifying Object-Oriented Programming - ZendCon 2016Demystifying Object-Oriented Programming - ZendCon 2016
Demystifying Object-Oriented Programming - ZendCon 2016
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 

More from Fwdays

More from Fwdays (20)

"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y..."How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
"How Preply reduced ML model development time from 1 month to 1 day",Yevhen Y...
 
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
"GenAI Apps: Our Journey from Ideas to Production Excellence",Danil Topchii
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets"What is a RAG system and how to build it",Dmytro Spodarets
"What is a RAG system and how to build it",Dmytro Spodarets
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi"Distributed graphs and microservices in Prom.ua",  Maksym Kindritskyi
"Distributed graphs and microservices in Prom.ua", Maksym Kindritskyi
 
"Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl..."Rethinking the existing data loading and processing process as an ETL exampl...
"Rethinking the existing data loading and processing process as an ETL exampl...
 
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T..."How Ukrainian IT specialist can go on vacation abroad without crossing the T...
"How Ukrainian IT specialist can go on vacation abroad without crossing the T...
 
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ..."The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
"The Strength of Being Vulnerable: the experience from CIA, Tesla and Uber", ...
 
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu..."[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
"[QUICK TALK] Radical candor: how to achieve results faster thanks to a cultu...
 
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care..."[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
"[QUICK TALK] PDP Plan, the only one door to raise your salary and boost care...
 
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"..."4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
"4 horsemen of the apocalypse of working relationships (+ antidotes to them)"...
 
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast..."Reconnecting with Purpose: Rediscovering Job Interest after Burnout",  Anast...
"Reconnecting with Purpose: Rediscovering Job Interest after Burnout", Anast...
 
"Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others..."Mentoring 101: How to effectively invest experience in the success of others...
"Mentoring 101: How to effectively invest experience in the success of others...
 
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova"Mission (im) possible: How to get an offer in 2024?",  Oleksandra Myronova
"Mission (im) possible: How to get an offer in 2024?", Oleksandra Myronova
 
"Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv..."Why have we learned how to package products, but not how to 'package ourselv...
"Why have we learned how to package products, but not how to 'package ourselv...
 
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin..."How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
"How to tame the dragon, or leadership with imposter syndrome", Oleksandr Zin...
 

Recently uploaded

+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@
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Recently uploaded (20)

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...
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
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?
 
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
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
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...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
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
 
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
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
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
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 

Nikola Poša "Handling exceptional conditions with grace and style"