How Kris Writes Symfony Apps

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
How Kris Writes Symfony Apps
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?
How Kris Writes Symfony Apps
How Kris Writes Symfony Apps
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
How Kris Writes Symfony Apps
<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
How Kris Writes Symfony Apps
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!
1 sur 106

Recommandé

How Kris Writes Symfony Apps par
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
17.1K vues116 diapositives
Drupal, meet Assetic par
Drupal, meet AsseticDrupal, meet Assetic
Drupal, meet AsseticKris Wallsmith
9.1K vues114 diapositives
Matters of State par
Matters of StateMatters of State
Matters of StateKris Wallsmith
16.4K vues87 diapositives
Love and Loss: A Symfony Security Play par
Love and Loss: A Symfony Security PlayLove and Loss: A Symfony Security Play
Love and Loss: A Symfony Security PlayKris Wallsmith
12.6K vues77 diapositives
Guard Authentication: Powerful, Beautiful Security par
Guard Authentication: Powerful, Beautiful SecurityGuard Authentication: Powerful, Beautiful Security
Guard Authentication: Powerful, Beautiful SecurityRyan Weaver
9.4K vues102 diapositives
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more par
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreRyan Weaver
32.1K vues114 diapositives

Contenu connexe

Tendances

jQuery Best Practice par
jQuery Best Practice jQuery Best Practice
jQuery Best Practice chandrashekher786
654 vues20 diapositives
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk) par
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)Dotan Dimet
1.4K vues32 diapositives
Mojolicious: what works and what doesn't par
Mojolicious: what works and what doesn'tMojolicious: what works and what doesn't
Mojolicious: what works and what doesn'tCosimo Streppone
2.7K vues12 diapositives
jQuery: Events, Animation, Ajax par
jQuery: Events, Animation, AjaxjQuery: Events, Animation, Ajax
jQuery: Events, Animation, AjaxConstantin Titarenko
1.4K vues21 diapositives
Building Large jQuery Applications par
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery ApplicationsRebecca Murphey
7.8K vues35 diapositives
Dojo Confessions par
Dojo ConfessionsDojo Confessions
Dojo ConfessionsRebecca Murphey
1.4K vues27 diapositives

Tendances(20)

Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk) par Dotan Dimet
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)
Dotan Dimet1.4K vues
Mojolicious: what works and what doesn't par Cosimo Streppone
Mojolicious: what works and what doesn'tMojolicious: what works and what doesn't
Mojolicious: what works and what doesn't
Cosimo Streppone2.7K vues
Building Large jQuery Applications par Rebecca Murphey
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
Rebecca Murphey7.8K vues
Decoupling the Ulabox.com monolith. From CRUD to DDD par Aleix Vergés
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
Aleix Vergés1.8K vues
Write Less Do More par Remy Sharp
Write Less Do MoreWrite Less Do More
Write Less Do More
Remy Sharp4.9K vues
DOM Scripting Toolkit - jQuery par Remy Sharp
DOM Scripting Toolkit - jQueryDOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQuery
Remy Sharp3.4K vues
jQuery 1.7 Events par dmethvin
jQuery 1.7 EventsjQuery 1.7 Events
jQuery 1.7 Events
dmethvin3.9K vues
Remy Sharp The DOM scripting toolkit jQuery par deimos
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuery
deimos1.3K vues
Mojolicious, real-time web framework par taggg
Mojolicious, real-time web frameworkMojolicious, real-time web framework
Mojolicious, real-time web framework
taggg1.6K vues
Joe Walker Interactivewebsites Cometand Dwr par deimos
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwr
deimos1.6K vues
HTML5: where flash isn't needed anymore par Remy Sharp
HTML5: where flash isn't needed anymoreHTML5: where flash isn't needed anymore
HTML5: where flash isn't needed anymore
Remy Sharp2.5K vues
Introducing Assetic: Asset Management for PHP 5.3 par Kris Wallsmith
Introducing Assetic: Asset Management for PHP 5.3Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3
Kris Wallsmith33.7K vues
Mulberry: A Mobile App Development Toolkit par Rebecca Murphey
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
Rebecca Murphey1.9K vues
Symfony: Your Next Microframework (SymfonyCon 2015) par Ryan Weaver
Symfony: Your Next Microframework (SymfonyCon 2015)Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)
Ryan Weaver7.6K vues

Similaire à How Kris Writes Symfony Apps

Doctrine For Beginners par
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
1.5K vues69 diapositives
DrupalCon jQuery par
DrupalCon jQueryDrupalCon jQuery
DrupalCon jQueryNathan Smith
1.8K vues55 diapositives
Migrare da symfony 1 a Symfony2 par
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2 Massimiliano Arione
930 vues23 diapositives
Absolute Beginners Guide to Puppet Through Types - PuppetConf 2014 par
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 2014Puppet
2K vues28 diapositives
Desymfony 2011 - Habemus Bundles par
Desymfony 2011 - Habemus BundlesDesymfony 2011 - Habemus Bundles
Desymfony 2011 - Habemus BundlesAlbert Jessurum
629 vues86 diapositives
Javascript is your (Auto)mate par
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mateCodemotion
1.1K vues63 diapositives

Similaire à How Kris Writes Symfony Apps(20)

Absolute Beginners Guide to Puppet Through Types - PuppetConf 2014 par Puppet
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
Puppet2K vues
Javascript is your (Auto)mate par Codemotion
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mate
Codemotion1.1K vues
How kris-writes-symfony-apps-london par Kris Wallsmith
How kris-writes-symfony-apps-londonHow kris-writes-symfony-apps-london
How kris-writes-symfony-apps-london
Kris Wallsmith3.7K vues
Drupal 8, Where Did the Code Go? From Info Hook to Plugin par Acquia
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
Acquia1.6K vues
KISSY 的昨天、今天与明天 par tblanlan
KISSY 的昨天、今天与明天KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天
tblanlan971 vues
kissy-past-now-future par yiming he
kissy-past-now-futurekissy-past-now-future
kissy-past-now-future
yiming he1.1K vues
Rails 3: Dashing to the Finish par Yehuda Katz
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
Yehuda Katz29.1K vues
Burn down the silos! Helping dev and ops gel on high availability websites par Lindsay Holmwood
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
Lindsay Holmwood1.6K vues
A re introduction to webpack - reactfoo - mumbai par Praveen Puglia
A re introduction to webpack - reactfoo - mumbaiA re introduction to webpack - reactfoo - mumbai
A re introduction to webpack - reactfoo - mumbai
Praveen Puglia266 vues
Bubbles & Trees with jQuery par Bastian Feder
Bubbles & Trees with jQueryBubbles & Trees with jQuery
Bubbles & Trees with jQuery
Bastian Feder1.4K vues
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・- par Tsuyoshi Yamamoto
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Tsuyoshi Yamamoto1.4K vues
How to actually use promises - Jakob Mattsson, FishBrain par Codemotion Tel Aviv
How to actually use promises - Jakob Mattsson, FishBrainHow to actually use promises - Jakob Mattsson, FishBrain
How to actually use promises - Jakob Mattsson, FishBrain

Plus de Kris Wallsmith

The View From Inside par
The View From InsideThe View From Inside
The View From InsideKris Wallsmith
2.5K vues22 diapositives
Assetic (Zendcon) par
Assetic (Zendcon)Assetic (Zendcon)
Assetic (Zendcon)Kris Wallsmith
1.8K vues107 diapositives
Assetic (OSCON) par
Assetic (OSCON)Assetic (OSCON)
Assetic (OSCON)Kris Wallsmith
1.2K vues127 diapositives
Assetic (Symfony Live Paris) par
Assetic (Symfony Live Paris)Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)Kris Wallsmith
4.9K vues129 diapositives
Introducing Assetic (NYPHP) par
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Kris Wallsmith
4K vues125 diapositives
Doctrine MongoDB ODM (PDXPHP) par
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Kris Wallsmith
2.1K vues49 diapositives

Plus de Kris Wallsmith(10)

Dernier

Future of Indian ConsumerTech par
Future of Indian ConsumerTechFuture of Indian ConsumerTech
Future of Indian ConsumerTechKapil Khandelwal (KK)
21 vues68 diapositives
Network Source of Truth and Infrastructure as Code revisited par
Network Source of Truth and Infrastructure as Code revisitedNetwork Source of Truth and Infrastructure as Code revisited
Network Source of Truth and Infrastructure as Code revisitedNetwork Automation Forum
25 vues45 diapositives
Case Study Copenhagen Energy and Business Central.pdf par
Case Study Copenhagen Energy and Business Central.pdfCase Study Copenhagen Energy and Business Central.pdf
Case Study Copenhagen Energy and Business Central.pdfAitana
16 vues3 diapositives
AMAZON PRODUCT RESEARCH.pdf par
AMAZON PRODUCT RESEARCH.pdfAMAZON PRODUCT RESEARCH.pdf
AMAZON PRODUCT RESEARCH.pdfJerikkLaureta
19 vues13 diapositives
Voice Logger - Telephony Integration Solution at Aegis par
Voice Logger - Telephony Integration Solution at AegisVoice Logger - Telephony Integration Solution at Aegis
Voice Logger - Telephony Integration Solution at AegisNirmal Sharma
31 vues1 diapositive
The details of description: Techniques, tips, and tangents on alternative tex... par
The details of description: Techniques, tips, and tangents on alternative tex...The details of description: Techniques, tips, and tangents on alternative tex...
The details of description: Techniques, tips, and tangents on alternative tex...BookNet Canada
126 vues24 diapositives

Dernier(20)

Case Study Copenhagen Energy and Business Central.pdf par Aitana
Case Study Copenhagen Energy and Business Central.pdfCase Study Copenhagen Energy and Business Central.pdf
Case Study Copenhagen Energy and Business Central.pdf
Aitana16 vues
Voice Logger - Telephony Integration Solution at Aegis par Nirmal Sharma
Voice Logger - Telephony Integration Solution at AegisVoice Logger - Telephony Integration Solution at Aegis
Voice Logger - Telephony Integration Solution at Aegis
Nirmal Sharma31 vues
The details of description: Techniques, tips, and tangents on alternative tex... par BookNet Canada
The details of description: Techniques, tips, and tangents on alternative tex...The details of description: Techniques, tips, and tangents on alternative tex...
The details of description: Techniques, tips, and tangents on alternative tex...
BookNet Canada126 vues
TouchLog: Finger Micro Gesture Recognition Using Photo-Reflective Sensors par sugiuralab
TouchLog: Finger Micro Gesture Recognition  Using Photo-Reflective SensorsTouchLog: Finger Micro Gesture Recognition  Using Photo-Reflective Sensors
TouchLog: Finger Micro Gesture Recognition Using Photo-Reflective Sensors
sugiuralab19 vues
Business Analyst Series 2023 - Week 3 Session 5 par DianaGray10
Business Analyst Series 2023 -  Week 3 Session 5Business Analyst Series 2023 -  Week 3 Session 5
Business Analyst Series 2023 - Week 3 Session 5
DianaGray10237 vues
Unit 1_Lecture 2_Physical Design of IoT.pdf par StephenTec
Unit 1_Lecture 2_Physical Design of IoT.pdfUnit 1_Lecture 2_Physical Design of IoT.pdf
Unit 1_Lecture 2_Physical Design of IoT.pdf
StephenTec12 vues
Attacking IoT Devices from a Web Perspective - Linux Day par Simone Onofri
Attacking IoT Devices from a Web Perspective - Linux Day Attacking IoT Devices from a Web Perspective - Linux Day
Attacking IoT Devices from a Web Perspective - Linux Day
Simone Onofri15 vues
GDG Cloud Southlake 28 Brad Taylor and Shawn Augenstein Old Problems in the N... par James Anderson
GDG Cloud Southlake 28 Brad Taylor and Shawn Augenstein Old Problems in the N...GDG Cloud Southlake 28 Brad Taylor and Shawn Augenstein Old Problems in the N...
GDG Cloud Southlake 28 Brad Taylor and Shawn Augenstein Old Problems in the N...
James Anderson66 vues
STKI Israeli Market Study 2023 corrected forecast 2023_24 v3.pdf par Dr. Jimmy Schwarzkopf
STKI Israeli Market Study 2023   corrected forecast 2023_24 v3.pdfSTKI Israeli Market Study 2023   corrected forecast 2023_24 v3.pdf
STKI Israeli Market Study 2023 corrected forecast 2023_24 v3.pdf
PharoJS - Zürich Smalltalk Group Meetup November 2023 par Noury Bouraqadi
PharoJS - Zürich Smalltalk Group Meetup November 2023PharoJS - Zürich Smalltalk Group Meetup November 2023
PharoJS - Zürich Smalltalk Group Meetup November 2023
Noury Bouraqadi126 vues

How Kris Writes Symfony Apps