SlideShare ist ein Scribd-Unternehmen logo
1 von 106
Downloaden Sie, um offline zu lesen
How Kris Writes Symfony Apps
@kriswallsmith
father artist bowhunter hacker
president widower gamer actor
tapdancer lover hater singer
writer founder yogi consultant
archer musician architect slacker
soccer player volunteer home
owner scotch drinker pianist…
assetic
Buzz
Spork
Getting Started
composer create-project !
symfony/framework-standard-edition !
widgets-by-kris/ !
~2.4
+
+
+
+

"doctrine/orm": "~2.2,>=2.2.3",!
"doctrine/doctrine-bundle": "~1.2",!
"doctrine/mongodb-odm-bundle": "~3.0",!
"jms/di-extra-bundle": "~1.4",!
"jms/security-extra-bundle": "~1.5",!
"jms/serializer-bundle": "~1.0",
./app/console generate:bundle !
--namespace=Kris/Bundle/MainBundle
public function registerContainerConfiguration(LoaderInterface $loader)!
{!
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');!
!
// load local_*.yml or local.yml!
if (!
file_exists($file = __DIR__.'/config/local_'.$this->getEnvironment().'.yml')!
||!
file_exists($file = __DIR__.'/config/local.yml')!
) {!
$loader->load($file);!
}!
}
Model
Treat your model like a princess.
She gets her own wing
of the palace…
doctrine_mongodb:!
auto_generate_hydrator_classes: %kernel.debug%!
auto_generate_proxy_classes:
%kernel.debug%!
connections: { default: ~ }!
document_managers:!
default:!
connection: default!
database:
kris!
mappings:!
model:!
type:
annotation!
dir:
%src%/Kris/Model!
prefix: KrisModel!
alias: Model
// repo for src/Kris/Model/Widget.php!
$repo = $this->dm->getRepository('Model:User');
…doesn't do any work…
use KrisBundleMainBundleCanonicalizer;!
!

public function setUsername($username)!
{!
$this->username = $username;!
!

$canonicalizer = Canonicalizer::instance();!
$this->usernameCanonical = $canonicalizer->canonicalize($username);!
}
use KrisBundleMainBundleCanonicalizer;!
!

public function setUsername($username, Canonicalizer $canonicalizer)!
{!
$this->username = $username;!
$this->usernameCanonical = $canonicalizer->canonicalize($username);!
}
…and is unaware of the work
being done around her.
public function setUsername($username)!
{!
// a listener will update the!
// canonical username!
$this->username = $username;!
}
Anemic domain model 

is an anti-pattern?
Anemic???
“The fundamental horror of this anti-pattern is that it's
so contrary to the basic idea of object-oriented design;
which is to combine data and process together.”	

!

Martin Fowler
$cabinet->open();
Cabinets don’t open themselves.
$asset->getLastModified();
Mapping Layers
thin
thin controller

fat model
MVC
Is Symfony an MVC framework?
HTTP
Application Land

Controller

HTTP Land
The controller is thin 

because it maps from 

HTTP-land to application-land.
What about the model?
public function registerAction()!
{!
// ...!
$user->sendWelcomeEmail();!
// ...!
}
public function registerAction()!
{!
// ...!
$mailer->sendWelcomeEmail($user);!
// ...!
}
Persistence Land

Model

Application Land
The model maps from
application-land to persistence-land.
Persistence Land

Model

Application Land

Controller

HTTP Land
Who lives in application land?
Thin controller, thin model…


Fat service layer!
Application Events
Use them.
That happened.
/** @DIObserve("user.username_change") */!
public function onUsernameChange(UserEvent $event)!
{!
$user = $event->getUser();!
$dm
= $event->getDocumentManager();!
!

$dm->getRepository('Model:Widget')!
->updateDenormalizedUsernames($user);!
}
One event class per model
•
•
•

Event name constants	

Accepts object manager and object as arguments	

Simple accessors
$event = new UserEvent($dm, $user);!
$dispatcher->dispatch(UserEvent::CREATE, $event);
$event = new UserUserEvent($dm, $user, $otherUser);!
$dispatcher->dispatch(UserEvent::FOLLOW, $event);
preFlush
public function preFlush(ManagerEventArgs $event)!
{!
$dm = $event->getObjectManager();!
$uow = $dm->getUnitOfWork();!
!
foreach ($uow->getIdentityMap() as $class => $docs) {!
if (is_a($class, 'KrisModelUser')) {!
foreach ($docs as $doc) {!
$this->processUserFlush($dm, $doc);!
}!
} elseif (is_a($class, 'KrisModelWidget')) {!
foreach ($docs as $doc) {!
$this->processWidgetFlush($dm, $doc);!
}!
}!
}!
}
/** @DIObserve("user.create") */!
public function onUserCreate(UserEvent $event)!
{!
$user = $event->getUser();!
!

$activity = new Activity();!
$activity->setActor($user);!
$activity->setVerb('register');!
$activity->setCreatedAt($user->getCreatedAt());!
!

$this->dm->persist($activity);!
}
/** @DIObserve("user.follow_user") */!
public function onFollowUser(UserUserEvent $event)!
{!
$event->getUser()!
->getStats()!
->incrementFollowedUsers(1);!
$event->getOtherUser()!
->getStats()!
->incrementFollowers(1);!
}
Decouple your application by
delegating work to clean, concise,
single-purpose event listeners.
Contextual Configuration
Save your future self a headache
# @MainBundle/Resources/config/widget.yml!
services:!
widget_twiddler:!
class: KrisBundleMainBundleWidgetTwiddler!
arguments:!
- @event_dispatcher!
- @?logger
JMSDiExtraBundle
/** @DIService("widget_twiddler") */!
class Twiddler!
{!
/** @DIInjectParams */!
public function __construct(!
EventDispatcherInterface $dispatcher,!
LoggerInterface $logger = null)!
{!
// ...!
}!
}
services:!
# aliases for auto-wiring!
container: @service_container!
dm: @doctrine_mongodb.odm.document_manager!
doctrine: @doctrine_mongodb!
dispatcher: @event_dispatcher!
security: @security.context
require.js
<script src="{{ asset('js/lib/require.js') }}"></script>!
<script>!
require.config({!
baseUrl: "{{ asset('js') }}",!
paths: {!
"jquery": "//ajax.googleapis.com/.../jquery.min",!
"underscore": "lib/underscore",!
"backbone": "lib/backbone"!
},!
shim: {!
"jquery": { exports: "jQuery" },!
"underscore": { exports: "_" },!
"backbone": {!
deps: [ "jquery", "underscore" ],!
exports: "Backbone"!
}!
}!
})!
require([ "main" ])!
</script>
// web/js/model/user.js!
define(!
[ "underscore", "backbone" ],!
function(_, Backbone) {!
var tmpl = _.template("<%- first %> <%- last %>")!
return Backbone.Model.extend({!
name: function() {!
return tmpl({!
first: this.get("first_name"),!
last: this.get("last_name")!
})!
}!
})!
}!
)
{% block head %}!
<script>!
require(!
[ "view/user", "model/user" ],!
function(UserView, User) {!
var view = new UserView({!
model: new User({{ user|serialize|raw }}),!
el: document.getElementById("user")!
})!
}!
)!
</script>!
{% endblock %}
Dependencies
•
•
•

model: backbone, underscore	

view: backbone, jquery	

template: model, view
{% javascripts!
"js/lib/jquery.js" "js/lib/underscore.js"!
"js/lib/backbone.js" "js/model/user.js"!
"js/view/user.js"!
filter="?uglifyjs2" output="js/packed/user.js" %}!
<script src="{{ asset_url }}"></script>!
{% endjavascripts %}!
!

<script>!
var view = new UserView({!
model: new User({{ user|serialize|raw }}),!
el: document.getElementById("user")!
})!
</script>
Unused dependencies
naturally slough off
JMSSerializerBundle
{% block head %}!
<script>!
require(!
[ "view/user", "model/user" ],!
function(UserView, User) {!
var view = new UserView({!
model: new User({{ user|serialize|raw }}),!
el: document.getElementById("user")!
})!
}!
)!
</script>!
{% endblock %}
/** @ExclusionPolicy("ALL") */!
class User!
{!
private $id;!
!

/** @Expose */!
private $firstName;!
!

/** @Expose */!
private $lastName;!
}
Miscellaneous
When to create a new bundle
•
•
•

Anything reusable	

Lots of classes relating to one feature	

Integration with a third party
{% include 'MainBundle:Account/Widget:sidebar.html.twig' %}
{% include 'AccountBundle:Widget:sidebar.html.twig' %}
Access Control
The Symfony ACL is for
arbitrary permissions
Encapsulate access logic in
custom voter classes
/** @DIService(public=false) @DITag("security.voter") */!
class WidgetVoter implements VoterInterface!
{!
public function supportsAttribute($attribute)!
{!
return 'OWNER' === $attribute;!
}!
!
public function supportsClass($class)!
{!
return is_a($class, 'KrisModelWidget');!
}!
!
public function vote(TokenInterface $token, $widget, array $attributes)!
{!
// ...!
}!
}
public function vote(TokenInterface $token, $widget, array $attributes)!
{!
$result = VoterInterface::ACCESS_ABSTAIN;!
if (!$this->supportsClass(get_class($widget))) {!
return $result;!
}!
!
foreach ($attributes as $attribute) {!
if (!$this->supportsAttribute($attribute)) {!
continue;!
}!
!
$result = VoterInterface::ACCESS_DENIED;!
if ($token->getUser() === $widget->getUser()) {!
return VoterInterface::ACCESS_GRANTED;!
}!
}!
!
return $result;!
}
JMSSecurityExtraBundle
/** @SecureParam(name="widget", permissions="OWNER") */!
public function editAction(Widget $widget)!
{!
// ...!
}
{% if is_granted('OWNER', widget) %}!
{# ... #}!
{% endif %}
No query builders
outside of repositories
class WidgetRepository extends DocumentRepository!
{!
public function findByUser(User $user)!
{!
return $this->createQueryBuilder()!
->field('userId')->equals($user->getId())!
->getQuery()!
->execute();!
}!
!
public function updateDenormalizedUsernames(User $user)!
{!
$this->createQueryBuilder()!
->update()!
->multiple()!
->field('userId')->equals($user->getId())!
->field('userName')->set($user->getUsername())!
->getQuery()!
->execute();!
}!
}
Eager ID creation
public function __construct()!
{!
$this->id = (string) new MongoId();!
}
public function __construct()!
{!
$this->id = (string) new MongoId();!
$this->createdAt = new DateTime();!
$this->widgets = new ArrayCollection();!
}
Remember your
clone constructor
$foo = new Foo();!
$bar = clone $foo;
public function __clone()!
{!
$this->id = (string) new MongoId();!
$this->createdAt = new DateTime();!
$this->widgets = new ArrayCollection(!
$this->widgets->toArray()!
);!
}
public function __construct()!
{!
$this->id = (string) new MongoId();!
$this->createdAt = new DateTime();!
$this->widgets = new ArrayCollection();!
}!
!

public function __clone()!
{!
$this->id = (string) new MongoId();!
$this->createdAt = new DateTime();!
$this->widgets = new ArrayCollection(!
$this->widgets->toArray()!
);!
}
Save space on field names
/** @ODMString(name="u") */!
private $username;!
!

/** @ODMString(name="uc") @ODMUniqueIndex */!
private $usernameCanonical;
Only flush from the controller
public function theAction(Widget $widget)!
{!
$this->get('widget_twiddler')!
->skeedaddle($widget);!
$this->flush();!
}
No proxy objects
/** @ODMReferenceOne(targetDocument="User") */!
private $user;
public function getUser()!
{!
if ($this->userId && !$this->user) {!
throw new UninitializedReferenceException('user');!
}!
!

return $this->user;!
}
Questions?
@kriswallsmith.net

https://joind.in/10371

Thank You!

Weitere ähnliche Inhalte

Was ist angesagt?

jQuery 1.7 Events
jQuery 1.7 EventsjQuery 1.7 Events
jQuery 1.7 Events
dmethvin
 
Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuery
deimos
 
Mojolicious, real-time web framework
Mojolicious, real-time web frameworkMojolicious, real-time web framework
Mojolicious, real-time web framework
taggg
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwr
deimos
 
Mulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
Rebecca Murphey
 

Was ist angesagt? (20)

jQuery Best Practice
jQuery Best Practice jQuery Best Practice
jQuery Best Practice
 
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
 
Mojolicious: what works and what doesn't
Mojolicious: what works and what doesn'tMojolicious: what works and what doesn't
Mojolicious: what works and what doesn't
 
jQuery: Events, Animation, Ajax
jQuery: Events, Animation, AjaxjQuery: Events, Animation, Ajax
jQuery: Events, Animation, Ajax
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
Dojo Confessions
Dojo ConfessionsDojo Confessions
Dojo Confessions
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Write Less Do More
Write Less Do MoreWrite Less Do More
Write Less Do More
 
DOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryDOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQuery
 
jQuery 1.7 Events
jQuery 1.7 EventsjQuery 1.7 Events
jQuery 1.7 Events
 
jQuery
jQueryjQuery
jQuery
 
jQuery in 15 minutes
jQuery in 15 minutesjQuery in 15 minutes
jQuery in 15 minutes
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuery
 
Mojolicious, real-time web framework
Mojolicious, real-time web frameworkMojolicious, real-time web framework
Mojolicious, real-time web framework
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwr
 
HTML5: where flash isn't needed anymore
HTML5: where flash isn't needed anymoreHTML5: where flash isn't needed anymore
HTML5: where flash isn't needed anymore
 
Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3
 
Mulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
 
Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)
 

Ähnlich wie How Kris Writes Symfony Apps

Desymfony 2011 - Habemus Bundles
Desymfony 2011 - Habemus BundlesDesymfony 2011 - Habemus Bundles
Desymfony 2011 - Habemus Bundles
Albert Jessurum
 
kissy-past-now-future
kissy-past-now-futurekissy-past-now-future
kissy-past-now-future
yiming he
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
Yehuda Katz
 

Ähnlich wie How Kris Writes Symfony Apps (20)

Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
DrupalCon jQuery
DrupalCon jQueryDrupalCon jQuery
DrupalCon jQuery
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
Absolute Beginners Guide to Puppet Through Types - PuppetConf 2014
Absolute Beginners Guide to Puppet Through Types - PuppetConf 2014Absolute Beginners Guide to Puppet Through Types - PuppetConf 2014
Absolute Beginners Guide to Puppet Through Types - PuppetConf 2014
 
Desymfony 2011 - Habemus Bundles
Desymfony 2011 - Habemus BundlesDesymfony 2011 - Habemus Bundles
Desymfony 2011 - Habemus Bundles
 
Javascript is your (Auto)mate
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mate
 
How kris-writes-symfony-apps-london
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-london
 
Having Fun with Play
Having Fun with PlayHaving Fun with Play
Having Fun with Play
 
Drupal 8, Where Did the Code Go? From Info Hook to Plugin
Drupal 8, Where Did the Code Go? From Info Hook to PluginDrupal 8, Where Did the Code Go? From Info Hook to Plugin
Drupal 8, Where Did the Code Go? From Info Hook to Plugin
 
Building Your First Widget
Building Your First WidgetBuilding Your First Widget
Building Your First Widget
 
KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天
 
kissy-past-now-future
kissy-past-now-futurekissy-past-now-future
kissy-past-now-future
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
 
A re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbaiA re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbai
 
Bubbles & Trees with jQuery
Bubbles & Trees with jQueryBubbles & Trees with jQuery
Bubbles & Trees with jQuery
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessor
 
The Beauty of Java Script
The Beauty of Java ScriptThe Beauty of Java Script
The Beauty of Java Script
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
How to actually use promises - Jakob Mattsson, FishBrain
How to actually use promises - Jakob Mattsson, FishBrainHow to actually use promises - Jakob Mattsson, FishBrain
How to actually use promises - Jakob Mattsson, FishBrain
 

Mehr von Kris Wallsmith

Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)
Kris Wallsmith
 

Mehr von Kris Wallsmith (10)

The View From Inside
The View From InsideThe View From Inside
The View From Inside
 
Assetic (Zendcon)
Assetic (Zendcon)Assetic (Zendcon)
Assetic (Zendcon)
 
Assetic (OSCON)
Assetic (OSCON)Assetic (OSCON)
Assetic (OSCON)
 
Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)
 
Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
A Practical Introduction to Symfony2
A Practical Introduction to Symfony2A Practical Introduction to Symfony2
A Practical Introduction to Symfony2
 
Symfony 2
Symfony 2Symfony 2
Symfony 2
 
Symfony in the Cloud
Symfony in the CloudSymfony in the Cloud
Symfony in the Cloud
 

Kürzlich hochgeladen

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
+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@
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 

Kürzlich hochgeladen (20)

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
+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...
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
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...
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 

How Kris Writes Symfony Apps