SlideShare ist ein Scribd-Unternehmen logo
1 von 116
Downloaden Sie, um offline zu lesen
How Kris Writes Symfony Apps
       @kriswallsmith • February 9, 2013
About Me
@kriswallsmith.net

•   Born, raised, & live in Portland

•   10+ years of experience

•   Lead Architect at OpenSky

•   Open source fanboy
brewcycleportland.com
assetic
Buzz
Spork
Go big or go home.
Getting Started
composer create-project 
    symfony/framework-standard-edition 
    opti-grab/ 2.2.x-dev
-   "doctrine/orm": "~2.2,>=2.2.3",
-   "doctrine/doctrine-bundle": "1.2.*",
+   "doctrine/mongodb-odm-bundle": "3.0.*",
+   "jms/serializer-bundle": "1.0.*",
./app/console generate:bundle 
    --namespace=OptiGrab/Bundle/MainBundle
assetic:
    debug:             %kernel.debug%
    use_controller:    false
    bundles:           [ MainBundle ]
    filters:
         cssrewrite:   ~
         uglifyjs2:    { compress: true, mangle: true }
         uglifycss:    ~
jms_di_extra:
    locations:
        bundles:
            - 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);
    }
}
MongoDB
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:     optiGrab
            mappings:
                 model:
                     type:    annotation
                     dir:     %src_dir%/OptiGrab/Model
                     prefix: OptiGrabModel
                     alias: Model
// repo for src/OptiGrab/Model/Widget.php
$repo = $this->dm->getRepository('Model:User');
…doesn't do any work…
use OptiGrabBundleMainBundleCanonicalizer;

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

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

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;
}
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()
    );
}
Only flush from the controller
public function theAction(Widget $widget)
{
    $this->get('widget_twiddler')
         ->skeedaddle($widget);
    $this->flush();
}
Save space on field names
/** @ODMString(name="u") */
private $username;

/** @ODMString(name="uc") @ODMUniqueIndex */
private $usernameCanonical;
public function getUsername()
{
    return $this->username ?: $this->usernameCanonical;
}

public function setUsername($username)
{
    if ($username) {
        $this->usernameCanonical = strtolower($username);
        $this->username = $username === $this->usernameCanonical ? null : $username;
    } else {
        $this->usernameCanonical = null;
        $this->username = null;
    }
}
No proxy objects
/** @ODMReferenceOne(targetDocument="User") */
private $user;
public function getUser()
{
    if ($this->userId && !$this->user) {
        throw new UninitializedReferenceException('user');
    }

    return $this->user;
}
Mapping Layers
What is a mapping layer?
A mapping layer is thin
Thin controller, fat model…
Is Symfony an MVC framework?
Symfony is an HTTP framework
Application Land
  Controller
  HTTP Land
The controller 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 lots of them
That happened.
/** @DIObserve("user.username_change") */
public function onUsernameChange(UserEvent $event)
{
    $user = $event->getUser();
    $dm   = $event->getDocumentManager();

    $dm->getRepository('Model:Widget')
       ->updateDenormalizedUsernames($user);
}
Unit of Work
public function onFlush(OnFlushEventArgs $event)
{
    $dm = $event->getDocumentManager();
    $uow = $dm->getUnitOfWork();

    foreach ($uow->getIdentityMap() as $class => $docs) {
        if (self::checkClass('OptiGrabModelUser', $class)) {
            foreach ($docs as $doc) {
                $this->processUserFlush($dm, $doc);
            }
        } elseif (self::checkClass('OptiGrabModelWidget', $class)) {
            foreach ($docs as $doc) {
                $this->processWidgetFlush($dm, $doc);
            }
        }
    }
}
private function processUserFlush(DocumentManager $dm, User $user)
{
    $uow     = $dm->getUnitOfWork();
    $meta    = $dm->getClassMetadata('Model:User');
    $changes = $uow->getDocumentChangeSet($user);

    if (isset($changes['id'][1])) {
        $this->dispatcher->dispatch(UserEvents::CREATE, new UserEvent($dm, $user));
    }

    if (isset($changes['usernameCanonical'][0]) && null !== $changes['usernameCanonical'][0]) {
        $this->dispatcher->dispatch(UserEvents::USERNAME_CHANGE, new UserEvent($dm, $user));
    }

    if ($followedUsers = $meta->getFieldValue($user, 'followedUsers')) {
        foreach ($followedUsers->getInsertDiff() as $otherUser) {
            $this->dispatcher->dispatch(
                UserEvents::FOLLOW_USER,
                new UserUserEvent($dm, $user, $otherUser)
            );
        }

        foreach ($followedUsers->getDeleteDiff() as $otherUser) {
            // ...
        }
    }
}
/** @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.create") */
public function onUserCreate(UserEvent $event)
{
    $dm   = $event->getDocumentManager();
    $user = $event->getUser();

    $widget = new Widget();
    $widget->setUser($user);

    $dm->persist($widget);

    // manually notify the event
    $event->getDispatcher()->dispatch(
        WidgetEvents::CREATE,
        new WidgetEvent($dm, $widget)
    );
}
/** @DIObserve("user.follow_user") */
public function onFollowUser(UserUserEvent $event)
{
    $event->getUser()
          ->getStats()
          ->incrementFollowedUsers(1);
    $event->getOtherUser()
          ->getStats()
          ->incrementFollowers(1);
}
Two event classes per model
• @MainBundleUserEvents: encapsulates event name constants
  such as UserEvents::CREATE and
  UserEvents::CHANGE_USERNAME

• @MainBundleEventUserEvent: base event object, accepts
  $dm and $user arguments

• @MainBundleWidgetEvents…
• @MainBundleEventWidgetEvent…
$event = new UserEvent($dm, $user);
$dispatcher->dispatch(UserEvents::CREATE, $event);
Delegate 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: OptiGrabBundleMainBundleWidgetTwiddler
        arguments:
            - @event_dispatcher
            - @?logger
/** @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
JMSDiExtraBundle
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
Lots of classes pertaining to
        one feature
{% 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 'OptiGrabModelWidget' === $class
            || is_subclass_of($class, 'OptiGrabModelWidget');
    }

    public function vote(TokenInterface $token, $widget, array $attributes)
    {
        // ...
    }
}
public function vote(TokenInterface $token, $map, array $attributes)
{
    $result = VoterInterface::ACCESS_ABSTAIN;

    if (!$this->supportsClass(get_class($map))) {
        return $result;
    }

    foreach ($attributes as $attribute) {
        if (!$this->supportsAttribute($attribute)) {
            continue;
        }

        $result = VoterInterface::ACCESS_DENIED;
        if ($token->getUser() === $map->getUser()) {
            return VoterInterface::ACCESS_GRANTED;
        }
    }

    return $result;
}
/** @SecureParam(name="widget", permissions="OWNER") */
public function editAction(Widget $widget)
{
    // ...
}
{% if is_granted('OWNER', widget) %}
{# ... #}
{% endif %}
Only mock interfaces
interface FacebookInterface
{
    function getUser();
    function api();
}

/** @DIService("facebook") */
class Facebook extends BaseFacebook implements FacebookInterface
{
    // ...
}
$facebook = $this->getMock('OptiGrabBundleMainBundleFacebookFacebookInterface');
$facebook->expects($this->any())
    ->method('getUser')
    ->will($this->returnValue(123));
Questions?
@kriswallsmith.net



        joind.in/8024


      Thank You!

Weitere ähnliche Inhalte

Was ist angesagt?

jQuery 1.7 Events
jQuery 1.7 EventsjQuery 1.7 Events
jQuery 1.7 Events
dmethvin
 
Jqeury ajax plugins
Jqeury ajax pluginsJqeury ajax plugins
Jqeury ajax plugins
Inbal Geffen
 
Kick start with j query
Kick start with j queryKick start with j query
Kick start with j query
Md. Ziaul Haq
 

Was ist angesagt? (20)

Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
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
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
Bacbkone js
Bacbkone jsBacbkone js
Bacbkone js
 
jQuery: Events, Animation, Ajax
jQuery: Events, Animation, AjaxjQuery: Events, Animation, Ajax
jQuery: Events, Animation, Ajax
 
DOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryDOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQuery
 
An Introduction to Jquery
An Introduction to JqueryAn Introduction to Jquery
An Introduction to Jquery
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code Organization
 
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
 
amsterdamjs - jQuery 1.5
amsterdamjs - jQuery 1.5amsterdamjs - jQuery 1.5
amsterdamjs - jQuery 1.5
 
Special Events
Special EventsSpecial Events
Special Events
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuery
 
jQuery 1.7 Events
jQuery 1.7 EventsjQuery 1.7 Events
jQuery 1.7 Events
 
Beyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsBeyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS Apps
 
Jqeury ajax plugins
Jqeury ajax pluginsJqeury ajax plugins
Jqeury ajax plugins
 
Dojo Confessions
Dojo ConfessionsDojo Confessions
Dojo Confessions
 
Jquery Fundamentals
Jquery FundamentalsJquery Fundamentals
Jquery Fundamentals
 
Kick start with j query
Kick start with j queryKick start with j query
Kick start with j query
 

Ähnlich wie How Kris Writes Symfony Apps

Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
Michael Peacock
 

Ähnlich wie How Kris Writes Symfony Apps (20)

Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
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
 
ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 Developer
 
BEAR DI
BEAR DIBEAR DI
BEAR DI
 
Drupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency InjectionDrupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency Injection
 
Symfony2 your way
Symfony2   your waySymfony2   your way
Symfony2 your way
 
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
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
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
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document Mapper
 
Et si on en finissait avec CRUD ?
Et si on en finissait avec CRUD ?Et si on en finissait avec CRUD ?
Et si on en finissait avec CRUD ?
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
 
Version Control with Puppet
Version Control with PuppetVersion Control with Puppet
Version Control with Puppet
 
PuppetCamp SEA 1 - Version Control with Puppet
PuppetCamp SEA 1 - Version Control with PuppetPuppetCamp SEA 1 - Version Control with Puppet
PuppetCamp SEA 1 - Version Control with Puppet
 

Mehr von Kris Wallsmith

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

Mehr von Kris Wallsmith (11)

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)
 
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
 
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

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
Enterprise Knowledge
 

Kürzlich hochgeladen (20)

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdf
 
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
 
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)
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
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
 
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
 
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
 
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
 
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
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
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...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 

How Kris Writes Symfony Apps