<?xml version="1.0" encoding="utf-8"?>
<!-- generator="Kirby" -->
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">

  <channel>
    <title>Blog Feed</title>
    <link>https://www.webguys.de</link>
    <generator>Kirby</generator>
    <lastBuildDate>Sat, 01 Jul 2017 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.webguys.de" rel="self" type="application/rss+xml" />

        <description>The latest updates from our blog</description>
    
        <item>
      <title>Web Engineering Unconference Europe wir kommen</title>
      <link>https://www.webguys.de/diverses/weuceu</link>
      <guid>diverses/weuceu</guid>
      <pubDate>Sat, 01 Jul 2017 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Nach vielen vielen Magento Events über die Jahre kehrt das Interesse manchmal doch wieder zu den guten alten Dingen zurück: PHP und Web-Tech. Das was wir alle Taten bevor es mit Magento los ging, erinnert ihr euch noch? :)</p>
<figure><img src="https://www.webguys.de/content/5-diverses/20170701-weuceu/teaser.png" alt="teaser"></figure>
<p>Da kommt die PHP-UnConf doch genau richtig, oder? Moment. Aber das ist ja nur PHP? Stimmt nicht mehr. Denn die PHP-Unconf findet unter neuer Flagge statt: <a href="https://weuceu.org/">Web Engineering unconference Europe</a>. </p>
<p>Nachdem in den letzten Jahren die Themen ohnehin sehr Web-Tech-Lastig, und somit häufig PHP unabhängig, waren ist der Schritt alle spannenden Techniken in einer Conference zu vereinen nur naheliegend. </p>
<p>Mit den Erfahrungen aus dem E-Commerce Camp in Jena, welches immerhin schon Shop-System unabhängig ist, viel es uns dann am Ende leicht zu entscheiden: Ja, wir sind dabei und bringen natürlich unser Team mit.</p>
<p>Achso, hatte ich erwähnt dass das Ticket nur 60€ kostet und der ganze Spaß auch noch in der Sonne auf Malle stattfindet?</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 24: Frohe Weihnachten &#38; Merry Christmas</title>
      <link>https://www.webguys.de/diverses/12-24-tuerchen-24-frohe-weihnachten-2016</link>
      <guid>diverses/12-24-tuerchen-24-frohe-weihnachten-2016</guid>
      <pubDate>Sat, 24 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>We wish you merry christmas, a happy new year - see you soon at some magento conferences :)</p>
<p>This year we filled the &quot;Türchen&quot;-list so fast as never before, thank you very much for all your help, great blog posts and sharing your knowledge. that is so awesome!</p>
<h2>Short review about all xmas-posts from 2016</h2>
<ul><li><a href="https://www.webguys.de/magento-1/profiling-is-the-key">Türchen 01: Profiling is the key</a></li><li><a href="https://www.webguys.de/magento-1/automated-deployment-of-magento1-shop-with-deployer">Türchen 02: Automated Deployment with deployer.org</a></li><li><a href="https://www.webguys.de/magento-1/lizards-and-pumpkins">Türchen 03: Solving the hardest problems</a></li><li><a href="https://www.webguys.de/magento-2/pdf-generierung-in-magento-2">Türchen 04: PDF Generierung in Magento 2</a></li><li><a href="https://www.webguys.de/magento-2/magento2-importexport-schnittstelle-unter-magento21">Türchen 05: ImportExport-Schnittstelle unter Magento2</a></li><li><a href="https://www.webguys.de/events/mageunconference-2017-reasons-to-join">Türchen 06: A riddle, a self-help group and Klebchen at the MageUnconference 2017...</a></li><li><a href="https://www.webguys.de/magento-2/magento2-api-where-to-start">Türchen 07: Magento 2 API - where to start and what has changed</a></li><li><a href="https://www.webguys.de/magento-1/tuerchen-08-simultaneous-deployment-with-php">Türchen 08: Simultaneous deployment to multiple nodes with PHP (deployer.php)</a></li><li><a href="https://www.webguys.de/diverses/building-an-it-team">Türchen 09: Building an IT team</a></li><li><a href="https://www.webguys.de/magento-1/codeception">Türchen 10: Frontend-Testing mit Codeception für Magento</a></li><li><a href="https://www.webguys.de/magento-1/schmalspurentwicklung-mit-magento-1">Türchen 11 : Schmalspurentwicklung von Magento 1 Shops (ohne geiles Deployment und so)</a></li><li><a href="https://www.webguys.de/diverses/you-can-sit-with-us">Türchen 12: You Can Sit With Us.</a></li><li><a href="https://www.webguys.de/diverses/how-to-mess-up-talking-on-stage">Türchen 13: How to mess up talking on stage.</a></li><li><a href="https://www.webguys.de/magento-2/understanding-the-blank-theme-of-magento-2">Türchen 14: Understanding the blank theme of Magento 2</a></li><li><a href="https://www.webguys.de/diverses/how-to-calculate-a-project">Türchen 15: How to calculate a project</a></li><li><a href="https://www.webguys.de/magento-1/what-happens-when-a-community-works-together">Türchen 16: What Happens When a Community Works Together</a></li><li><a href="https://www.webguys.de/magento-2/better-organize-your-dependencies-and-even-your-source-code-with-composer">Türchen 17: Better organize your dependencies and even your source code with composer</a></li><li><a href="https://www.webguys.de/magento-2/magento-2-for-magento1-developers">Türchen 18: Magento 2 for Magento 1 Developers</a></li><li><a href="https://www.webguys.de/magento-1/worst-magento-practice">Türchen 19: Worst Magento Practice</a></li><li><a href="https://www.webguys.de/magento-1/tuerchen-20-how-to-create-a-custom-lizards-and-pumpkins-rest-api-endpoint">Türchen 20 : How to create a custom Lizards & Pumpkins REST API endpoint</a></li><li><a href="https://www.webguys.de/diverses/transformation-vom-entwickler-zum-manager">Türchen 21: Die Transformation vom Entwickler zum Unternehmer</a></li><li><a href="https://www.webguys.de/magento-2/blackhat.design">Türchen 22: Blackhat.Design: Weapons of Social Seduction</a></li><li><a href="https://www.webguys.de/magento-1/easytemplate-eine-kurze-vorstellung">Türchen 23: Easytemplate - eine kurze Vorstellung</a></li><li><a href="https://www.webguys.de/diverses/12-24-tuerchen-24-frohe-weihnachten-2016">Türchen 24: Frohe Weihnachten & Merry Christmas</a></li></ul>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 23: Easytemplate - eine kurze Vorstellung</title>
      <link>https://www.webguys.de/magento-1/easytemplate-eine-kurze-vorstellung</link>
      <guid>magento-1/easytemplate-eine-kurze-vorstellung</guid>
      <pubDate>Fri, 23 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<h2>Warum Easy template</h2>
<p>Viele Themes bringen teilweise echt gut gestaltete Seiten mit, die aus einzelnen Komponenten zusammengesetzt sind und im Idealfall auch einfach adaptiert und wieder verwendet werden können. Schaut man sich in einem solchem Theme die betreffende Seite im Editor und die teilweise endlosen Zeilen HTML-Codes an, bedarf es einer gehörigen Portion Wissen um die HTML-Syntax und des Baukastensystems des Themes um die Seite zu verändern und/oder neue Seiten anzulegen.</p>
<p>Dies zu vereinfachen und so uns und den Kunden in der Lage zu versetzen, schnell Seiten zu erstellen und Inhalte zu pflegen war der Ansporn für Easytemplate.</p>
<h2>Easy template im Überblick</h2>
<p>Easy template ist ein Baukastensystem aus unterschiedlichen, endlos erweiterbaren Bausteinen aus dem der Shopbetreiber wählen kann, um Inhalte zu pflegen.</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161223-easytemplate-eine-kurze-vorstellung/information.png" alt="information"></figure>
<p>Klickt man auf den neuen Reiter 'Easy template' gelangt man zum neuen Inhaltsbereich.</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161223-easytemplate-eine-kurze-vorstellung/inhaltsbereich.png" alt="inhaltsbereich"></figure>
<p>Hier kann man gut erkennen, dass die Inhalte modul gepflegt werden, gekapselt und übersichtlich vorliegen.</p>
<p>Ein neuer Baustein kann über 'Neue Vorlage' eingefügt werden.Aus einer kategorisierbaren Liste kann der entsprechende Baustein ausgewählt werden, welcher dann am Anfang der Seite eingefügt wird.</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161223-easytemplate-eine-kurze-vorstellung/vorlagenliste.png" alt="vorlagenliste"></figure>
<p>Die Bausteine können einfach per Drag'n Drop verschoben werden, des weiteren können einzelne Bausteine deaktiviert oder auch nur für einen Zeitraum aktiviert werden. So lassen sich z.b. Inhalte im Voraus planen und nicht immer manuell eingeriffen werden.</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161223-easytemplate-eine-kurze-vorstellung/baustein.png" alt="baustein"></figure>
<p>Diese Maske wird durch eine XML-Datei erzeugt, in der alle Bausteine definiert sind.</p>
<pre><code>...
                &lt;productlisting_as_multiple_carousel template="easytemplate/home/product_list.phtml" type="easytemplate/template_product_skulist" translate="label,comment"&gt;
                    &lt;enabled&gt;1&lt;/enabled&gt;
                    &lt;label&gt;Produktliste als Karussell&lt;/label&gt;
                    &lt;sort_order&gt;40&lt;/sort_order&gt;
                    &lt;fields&gt;
                        &lt;global_headline translate="label,comment"&gt;
                            &lt;label&gt;Überschrift&lt;/label&gt;
                            &lt;input_renderer&gt;easytemplate/input_renderer_text&lt;/input_renderer&gt;
                            &lt;sort_order&gt;10&lt;/sort_order&gt;
                            &lt;required&gt;0&lt;/required&gt;
                        &lt;/global_headline&gt;

                        &lt;global_headline_type translate="label,comment"&gt;
                            &lt;label&gt;Titel-Typ&lt;/label&gt;
                            &lt;comment /&gt;
                            &lt;input_renderer&gt;easytemplate/input_renderer_select&lt;/input_renderer&gt;
                            &lt;input_renderer_source&gt;easytemplate/input_renderer_source_values&lt;/input_renderer_source&gt;
                            &lt;values&gt;
                                &lt;h1&gt;H1&lt;/h1&gt;
                                &lt;h2&gt;H2&lt;/h2&gt;
                                &lt;h3&gt;H3&lt;/h3&gt;
                                &lt;h4&gt;H4&lt;/h4&gt;
                                &lt;h5&gt;H5&lt;/h5&gt;
                            &lt;/values&gt;
                            &lt;sort_order&gt;15&lt;/sort_order&gt;
                            &lt;default_value&gt;h2&lt;/default_value&gt;
                            &lt;required&gt;0&lt;/required&gt;
                        &lt;/global_headline_type&gt;
                        &lt;skus translate="label,comment"&gt;
                            &lt;label&gt;Artikelnummern&lt;/label&gt;
                            &lt;input_renderer&gt;easytemplate/input_renderer_text&lt;/input_renderer&gt;
                            &lt;sort_order&gt;20&lt;/sort_order&gt;
                            &lt;comment&gt;Kommagetrennte Angabe mehrerer Artikelnummern&lt;/comment&gt;
                            &lt;required&gt;0&lt;/required&gt;
                        &lt;/skus&gt;
                        &lt;background-color translate="label,comment"&gt;
                            &lt;label&gt;Hintergrundfarbe&lt;/label&gt;
                            &lt;input_renderer&gt;easytemplate/input_renderer_text&lt;/input_renderer&gt;
                            &lt;sort_order&gt;30&lt;/sort_order&gt;
                            &lt;required&gt;0&lt;/required&gt;
                        &lt;/background-color&gt;
                    &lt;/fields&gt;
                &lt;/productlisting_as_multiple_carousel&gt;
...</code></pre>
<p>An diesem Beispiel kann man erkennen, dass es unterschiedliche Renderer gibt, die die Anzeige in Magento steuern und nahezu alle erdenktlichen Fälle abbilden können.</p>
<p>In der ersten Zeile wird dem Baustein das entsprechende Template zugewiesen.</p>
<p>Das Template des Baustein ist wie folgt aufgebaut:</p>
<pre><code>&lt;?php /** @var $this Webguys_Easytemplate_Block_Template_Product_Skulist */ ?&gt;

&lt;?php $uniqid = uniqid(); ?&gt;

&lt;div class="easy-module product_list" style="background-color: &lt;?php echo $this-&gt;getData('background-color');?&gt;"&gt;

    &lt;div class="main-container"&gt;
        &lt;div class="row"&gt;
            &lt;div class="col-xs-12-12"&gt;

                    &lt;?php if ($this-&gt;getData('global_headline')) : ?&gt;
                        &lt;&lt;?php echo $this-&gt;getData('global_headline_type');?&gt; class="h2"&gt;
                            &lt;?php echo $this-&gt;getData('global_headline'); ?&gt;
                        &lt;/&lt;?php echo $this-&gt;getData('global_headline_type');?&gt;&gt;
                    &lt;?php endif; ?&gt;

                &lt;?php if ($skus = $this-&gt;getData('skus')): ?&gt;

                    &lt;div class="easytemplate_product_carousel_container"&gt;
                        &lt;?php echo $this-&gt;getProductListBlock($skus)-&gt;setTemplate('easytemplate/products/list.phtml')-&gt;toHtml(); ?&gt;
                    &lt;/div&gt;

                &lt;?php endif; ?&gt;
            &lt;/div&gt;
        &lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;</code></pre>
<p>Die atomaren Eingaben in der Pflegemaske werden einfach über den im XML eingegeben Bezeichner angesprochen und die Werte können ausgegeben und/oder weiter verarbeitet werden.</p>
<pre><code>$this-&gt;getData('global_headline');</code></pre>
<p>Das ist schon mal ein toller Anfang, aber es wird noch besser. Die Felder der Maske werden über die XMl-Datei festgelegt. Spätestens wenn man jedoch einen Slider für einen Kunden baut und diese auf z.B. drei Bilder begrenzt, wird sich der Kunde vertrauensvoll an euch wenden, weil er gerne min. vier Bilder präsentieren möchte.</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161223-easytemplate-eine-kurze-vorstellung/extended_slider.png" alt="extended_slider"></figure>
<p>Hier können wir sehen, dass der Baustein 'Headvisual' keine Eingabefelder für die Slides bietet, sondern die Möglichkeit eröffnet, Sub-Bausteine hinzuzufügen. Somit lässt sich die Übersichtlichkeit signifikant erhöhen, ebenso wird der Shopbetreiber nicht von unzähligen Eingabefeldern erschlagen.</p>
<h2>Easy template im Laufenden Betrieb</h2>
<p>Während der Arbeit erleichert die Anlage einer Arbeitskopie die Arbeit ungemein. Möchte man eine Seite verändern, so wissen wir aus der täglichen Arbeit mit Magento, dass die Änderungen nach Klick auf 'Speichern' sofort online sind. Das macht die Arbeit nicht unbedingt besser. Den Workaround, eine Test-Seite anzulegen und dort die Änderungen vorzunehmen, ist auch nur bedingt komfortabel. Aus diesem Grund gibt es die Funktion 'Arbeitskopie anlegen' - diese legt eine temporäre Kopie der Seite an.</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161223-easytemplate-eine-kurze-vorstellung/arbeitskopie.png" alt="arbeitskopie"></figure>
<p>Man hat jetzt genügend Zeit Änderungen vorzunehmen bis man zufrieden ist.</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161223-easytemplate-eine-kurze-vorstellung/preview_arbeitskopie.png" alt="preview_arbeitskopie"></figure>
<p>Ist der Status der Glückseligkeit erreicht, reicht ein Klick auf 'Arbeitskopie veröffentlichen', um die Änderungen zu übernehmen. Dies geschieht ohne, das der Besucher im Shop etwas von den Arbeiten sieht.</p>
<h2>Easy template im Deployment</h2>
<p>Nicht selten kommt es vor, dass ein Update, welches auf einem System vorbereitet wird, über optische Änderungen verfügt, welche neben dem anderen Änderungen übertragen werden müssen. Bei technischen Änderungen ist dies kein Problem, über Versionierung und entsprechende Setup-Skripte lässt sich das meiste schnell in den Griff bekommen.</p>
<p>Anders sind hier jedoch Inhalte, die in Seiten oder Blöcken benötigt werden, hier kann man schneller mal den Überblick verlieren. Aus diesem Grund bietet Easy template die Möglichkeit die kompletten Bausteine einer Seite oder eines Blockes als Zip herunter zu laden und im Live-System wieder einzufügen - mit allen Inhalten selbstverständlich.</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161223-easytemplate-eine-kurze-vorstellung/download.png" alt="download"></figure>
<h2>Open-Source auf Github</h2>
<p>Natürlich gibt es EasyTemplate frei auf Github unter <a href="https://github.com/webguys-de/EasyTemplate">https://github.com/webguys-de/EasyTemplate</a> - die Portierung zu Magento 2 hatten wir schon einmal angefangen - ist dann aber ins Stocken geraten. Aktuell haben wir uns noch keinen genauen Zeitplan gesetzt. Aber vlt. hat der eine oder andere Lust und Zeit sich an einer Portierung zu beteiligen.</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 22: Blackhat.Design: Weapons of Social Seduction</title>
      <link>https://www.webguys.de/magento-2/blackhat.design</link>
      <guid>magento-2/blackhat.design</guid>
      <pubDate>Thu, 22 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p><strong>What are you going to do about that</strong>?</p>
<figure><img src="https://www.webguys.de/content/3-magento-2/20161222-blackhat.design/Blackhat-Design.png" alt="Blackhat-Design"></figure>
<p>A “Blackhat.Design” is a UI intentionally designed to ‘trick’ people into making choices that benefit the company, but are (usually) bad for the customer.</p>
<p>In my work as a psychologist and conversion optimization specialist I help webshops implement these persuasion tactics in online marketing campaigns. But there is also an ethical responsibility to give the audiences of these organisations the tools to guard themselves against these techniques for when they are misused.</p>
<p>If you want a more elaborate introduction: <a href="https://blackhat.design/blackhat-design-aa14767a85bc">Read this on Blackhat.Design first</a>.</p>
<p>In this post, I’m going to give you 3 examples of common tactics to help you recognize them and make better choices. In other words: to give you some Counter Persuasion Tactics (CPT).</p>
<h2>Easy too get in, hard to get out</h2>
<p>One trick called ‘Roach Motel’ is to make it really easy to sign up for a commitment, but REALLY HARD to get out. Some examples from nature:</p>
<figure><img src="https://www.webguys.de/content/3-magento-2/20161222-blackhat.design/easy-hard-1.jpg" alt="easy-hard-1"></figure>
<figure><img src="https://www.webguys.de/content/3-magento-2/20161222-blackhat.design/easy-hard-2.jpg" alt="easy-hard-2"></figure>
<figure><img src="https://www.webguys.de/content/3-magento-2/20161222-blackhat.design/easy-hard-3.jpg" alt="easy-hard-3"></figure>
<p>Take <a href="https://www.texture.com">Texture</a>, selling digital magazine subscriptions. This is all you need to sign up: </p>
<figure><img src="https://www.webguys.de/content/3-magento-2/20161222-blackhat.design/texture-create.png" alt="texture-create"></figure>
<p>So far so good. Now let’s see what it takes to unsubscribe:</p>
<figure><img src="https://www.webguys.de/content/3-magento-2/20161222-blackhat.design/texture-out.jpg" alt="texture-out"></figure>
<p>Really? I wonder why they didn’t go with the morsecode option…</p>
<p>There is no technological reason to not make unsubscribing just as easy as signing up. This is pure evil to keep people subscribed.</p>
<h2>Limited rooms available (Scarcity)</h2>
<p>When there is not much left of something, we want it even more.<br />
Originally Dutch booking site Booking.com got called back by a judge for mis/overusing this tactic.</p>
<figure><img src="https://www.webguys.de/content/3-magento-2/20161222-blackhat.design/booking-scarcity.png" alt="booking-scarcity"></figure>
<p>Sentences like “Only 2 rooms left” make you think that there are only 2 rooms left in the whole hotel, while it just meant that there were only 2 rooms left on booking.com (that day…). If you’d call the hotel, you might find out that a lot more rooms were actually available.</p>
<h2>Hiding important information</h2>
<p>At Euroflorist some of our usability research showed that users could hardly find the delivery fee and when they did said it came across untrustworthy as it looked like we were hiding it:</p>
<figure><img src="https://www.webguys.de/content/3-magento-2/20161222-blackhat.design/hidden-fees.png" alt="hidden-fees"></figure>
<p>Original on the left, improved version on the right.</p>
<p>(Technically not a Blackhat.Design because it wasn’t intentional in our case, but a lot of companies try to hide information in the fine print that they think might not be beneficial for the purchase.)</p>
<p>Not coming across as trustworthy is not a good long-term strategy. In this case, it’s even a very bad short-term strategy: the version on the right (where the delivery fee was placed directly below the product price) had a <strong>conversion rate increase of over 12%</strong>.</p>
<h3>Want more Counter Persuasion Tactics?</h3>
<p><strong>Subscribe to <a href="http://blackhat.design">blackhat.design</a>, I’ll be posting more next year, including an overall defence plan you can use for you next big purchase!</strong> (:</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 21: Die Transformation vom Entwickler zum Unternehmer</title>
      <link>https://www.webguys.de/diverses/transformation-vom-entwickler-zum-manager</link>
      <guid>diverses/transformation-vom-entwickler-zum-manager</guid>
      <pubDate>Wed, 21 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>In den vergangenen Jahren habe ich immer wieder Selbstständige und Unternehmer kennengelernt, die einige Jahre nach der Gründung - trotz gut laufender Geschäfte - gescheitert sind oder sich bewusst verkleinert haben.<br />
Doch weshalb kommt es dazu und was hat das mit der Transformation vom Entwickler zum Unternehmer zu tun?</p>
<h2>Definition der Formen einer Selbstständigkeit</h2>
<p>Für mich gibt es in unserer Branche 2 relevante Formen der Selbstständigkeit, die der selbstständigen Fachkraft und die des Unternehmers. Doch wie unterscheiden sich die beiden?</p>
<h3>Die selbständige Fachkraft (Freelancer)</h3>
<p>Ein Freelancer muss sich um alle Bereiche im Unternehmen kümmern (auch wenn dieses nur aus ihr selbst besteht) und muss sich so ein breites Wissen über das eigentliche Fachwissen hinaus aneignen. Zumeist handelt es sich um eine Person, die das Arbeiten in ihrem Fachbereich liebt und niemals aufgeben möchte.</p>
<h3>Der Unternehmer</h3>
<p>Der Unternehmer arbeitet nicht als Fachkraft im Unternehmen, sondern am Unternehmen selbst. Er beschäftigt sich mit der langfristigen Planung und strategischen Ausrichtung des Unternehmens und trägt i.d.R. das Gesamtrisiko. Der Unternehmer wird in den unterschiedlichen Bereichen seines Unternehmens von seinen Mitarbeitern unterstützt.</p>
<h3>Wie alles begann</h3>
<p>So begann auch ich gemeinsam mit meinem Bruder Jonas 2012 mit der Gründung unserer Internet Agentur die Reise. Du diesem Zeitpunkt waren wir 2 junge Magento Entwickler, die sich in Form einer Agentur zusammengeschlossen hatten und klassische selbstständige Fachkräfte waren. Für uns war jedoch von Anfang an klar, dass wir uns zu Unternehmern weiterentwickeln möchten und eine erfolgreiche Agentur aufbauen möchten.</p>
<p><strong>Tipps</strong></p>
<ul>
<li>Bevor Du Dich selbstständig machst solltest Du Deine Ziele definieren (Das muss kein kompletter Businessplan sein)</li>
<li>Wenn möglich: Suche Dir einen Partner, dessen Interessen sich mit Deinen ergänzen</li>
<li>Probiere es einfach aus, meist hast du nichts zu verlieren</li>
</ul>
<p>In dieser ersten Phase entstehen meist noch keine Probleme. Man hat ein paar Kunden, kann seiner Leidenschaft nachgehen und erhält dafür eine gute Entlohnung.</p>
<h3>Das Wachstum</h3>
<p>Auf diese erste Phase folgen die ersten Rückschläge und Zweifel. Diese können vielfältig sein, der große Kunde ist weggefallen, neue Projekte sind gerade nicht in Aussicht, die ersten schlechten Erfahrungen mit Mitarbeitern entstehen ... - was mache ich nun?<br />
Zunächst einmal: Ruhe bewahren und reflektieren was schiefgelaufen ist? Fehler erkennen, korrigieren und nach vorne schauen.</p>
<p>Mit diesem ersten Wachstum kommt meiner Meinung nach auch die erste große Entscheidung. Möchte ich Unternehmer werden oder Fachkraft bleiben?</p>
<p><strong>Tipps</strong></p>
<ul>
<li>Gehe in Dich und frage Dich selbst: Was sind meine Stärken, Schwächen und Ziele?</li>
<li>Möchtest Du selbst das Fachwissen deiner Tätigkeit haben (Fachkraft)?</li>
<li>Möchtest du Dein Fachwissen Deiner Tätigkeit an Mitarbeiter weitergeben/abtreten und Dich aus den Fachaufgaben zurückziehen (Unternehmer)?</li>
<li>Sprich mit Leuten, die diesen Weg bereits gegangen sind</li>
</ul>
<p>In dieser Phase kommen neben/nach den oben beschriebenen Problemen meist mehr Aufträge auf einen zu, sodass man diese nicht mehr selbstständig bewältigen kann. Somit beginnt man das Unternehmen auszubauen, Mitarbeiter einzustellen oder mit Freelancern zusammenzuarbeiten.</p>
<h3>Und plötzlich machst du etwas ganz Anderes</h3>
<p>Bis zu einer gewissen Größe kann man dieses Wachstum auch als Fachkraft durchziehen, doch plötzlich spürt man eine gewisse Unzufriedenheit, die man gar nicht so genau definieren kann. Die Mitarbeiter sind fleißig, die Projekte erfolgreich und es kommen genug neue Anfragen.</p>
<p>Zu diesem Zeitpunkt steht man vor der ersten Transformation. Die eigentliche Fachtätigkeit rückt immer stärker in den Hintergrund und man ist hauptsächlich damit beschäftigt, seine Mitarbeiter und die Projekte zu koordinieren.<br />
Dies ist der Zeitpunkt, um wieder eine Entscheidung zu treffen, denn trifft man diese nicht, arbeitet man gegen sich selbst, da man als Fachkraft und Manager verschiede Ansichten und Ziele vertritt. Wenn Du mehr zu diesem Thema erfahren möchtest, empfehle ich das Buch &quot;Der Weg zum erfolgreichen Unternehmer&quot; von Stefan Merath.</p>
<p>Genau diese Phasen (sie kommen nicht von heute auf morgen) zeichnen den Punkt, an dem einige scheitern oder sich bewusst dazu entscheiden, sich wieder auf ihren ursprünglichen Fachbereich zu konzentrieren. Denn, wenn man immer in einem Zwiespalt zwischen Fachkraft, Manager und Unternehmer steckt, wird man auf Dauer nicht glücklich und fragt sich oft: was mache ich hier eigentlich?</p>
<p><strong>Tipp</strong></p>
<ul>
<li>Versuche frühzeitig einzulenken, wenn Du merkst, dass Dein Weg Dich nicht erfüllt</li>
<li>Entscheide, ob Du Dein Unternehmen anpasst oder suche Dir Mitarbeiter, die Dich unterstützen</li>
<li>Triff eine Entscheidung, sonst treffen sie andere für Dich</li>
</ul>
<p>Es gibt viele Möglichkeiten diesen Weg weiter zu gehen, ich habe Menschen kennengelernt, die wieder zurück in ihren Fachbereich gegangen sind und die Führung ihren Partnern überlassen haben, denen das Unternehmer-Dasein mehr zusagte. Andere haben sich entschlossen ihre Agenturen zu verkaufen oder auf eine Größe zu reduzieren, in der sie als Fachkraft mitarbeiten können.</p>
<p>Einen falschen Weg gibt es letztendlich nicht, die Entscheidung, welcher der Richtige ist, kannst Du nur alleine treffen.</p>
<h3>Mein Weg</h3>
<p>Ich persönlich habe mich dazu entschieden, Unternehmer zu werden. Ich sehe es als neue Herausforderung in meinem Leben und eine Chance neue, spannende Dinge kennenzulernen. Noch stecke ich mitten in der Wachstumsphase und bin Fachkraft, Projektmanager und Unternehemer.<br />
Durch diese doch sehr unterschiedlichen Aufgaben, kenne ich das Gefühl &quot;Was habe ich jetzt eigentlich genau heute gearbeitet?&quot; sehr gut. Hier ist mein Tipp, ein klares Ziel vor Augen zu haben und auf dieses hinzuarbeiten.</p>
<p>Ein hilfreiches Werkzeug, das während des ersten Wachstums gerne vergessen wird ist der Aufbau von Prozessen und Standards. Langfristig sind diese jedoch die einzige Möglichkeit eine Agentur, die wächst erfolgreich zu führen und eine gleichbleibende Qualität zu gewährleisten.<br />
Wer nur wächst und abarbeitet wird irgendwann an einen Punkt kommen, an dem im besten Fall das Wachstum nur stagniert, im schlimmsten Fall aber das gesamte Unternehmen zusammenbricht.</p>
<p>Wir selbst haben dieses Jahr unsere Prozesse stark optimiert und unserem Wachstum angepasst, diese sind an der ein oder anderen Stelle aktuell noch etwas &quot;zu groß&quot;, noch ist eine Prozesseinführung leicht durchzuführen und wirkt sich positiv auf ein kontinuierliches und gesundes Wachstum aus.</p>
<p>Wenn Du Dich entschieden hast, Unternehmer zu werden, habe ich noch ein paar Tipps wie Dir Dir das Leben leichter machen kannst.</p>
<p><strong>Tipps</strong></p>
<ul>
<li>Setze Dir klar definierte Ziele</li>
<li>Priorisiere Deine Aufgaben</li>
<li>Lerne Aufgaben abzugeben und diese nicht irgendwann selbst zu übernehmen (egal wie oft Du sie zurückgeben musst)</li>
<li>Suche Dir gleichgesinnte und lerne von Menschen mit Erfahrung (Aufbau eines Netzwerks)</li>
<li>Probiere Dinge aus, mache Fehler und lerne daraus</li>
</ul>
<p>Ich hoffe, ich konnte Dir einen Einblick in die Transformation geben und bei einer vielleicht anstehenden Entscheidung helfen. Solltest Du Fragen haben oder noch mehr wissen wollen, kannst Du mich gerne anschreiben.</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 20 : How to create a custom Lizards &#38; Pumpkins REST API endpoint</title>
      <link>https://www.webguys.de/magento-1/tuerchen-20-how-to-create-a-custom-lizards-and-pumpkins-rest-api-endpoint</link>
      <guid>magento-1/tuerchen-20-how-to-create-a-custom-lizards-and-pumpkins-rest-api-endpoint</guid>
      <pubDate>Tue, 20 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>One of core principles of Lizards &amp; Pumpkins and what makes it extremely attractive among e-commerce platforms is the API-first approach. The business logic is completely front-end agnostic and can be used for feeding either website, mobile app or anything else. And as the name suggests the binding point here is an API, a REST API in our case.</p>
<p>Native REST API of Lizards &amp; Pumpkins already provides a rich set of endpoints. However for some very implementation specific functionality you may want to create a custom endpoint. Bestsellers or some custom cross-sells are first that comes to mind. Luckily implementing custom REST API endpoint is extremely easy in Lizards.</p>
<h3>Service</h3>
<p>The first thing to do is to create a service which will return a requested data. Let’s call it <code>BestsellersService</code>. It will have just one public method <code>getBestsellers</code> which will return an array of bestselling product data.</p>
<p>So let’s start with most degenerative test:</p>
<pre><code class="language-php">final protected function setUp()
{
   $this-&gt;mockDataPoolReader = $this-&gt;createMock(DataPoolReader::class);
   $this-&gt;stubProductJsonService = $this-&gt;createMock(ProductJsonService::class);

   $this-&gt;bestsellerService = new BestsellerService($this-&gt;mockDataPoolReader, $this-&gt;stubProductJsonService);

   $this-&gt;stubContext = $this-&gt;createMock(Context::class);
}

public function testReturnsAnEmptyArrayIfNoProductsMatchBestsellingCriteria()
{
   $this-&gt;mockDataPoolReader-&gt;method('getProductIdsMatchingCriteria')-&gt;willReturn([]);
   $this-&gt;assertSame([], $this-&gt;bestsellerService-&gt;getBestsellers($this-&gt;stubContext));
}</code></pre>
<p>For the sake of readability the property declarations are omitted.</p>
<p>As you can see our service will take a <code>DataPoolReader</code> and <code>ProductJsonService</code> as constructor arguments. The later will create a JSON representation of product IDs returned by the former.</p>
<p>The <code>Context</code> stub will be used in every test so let’s also put into a class property. The <code>Context</code> is a nifty bastard that holds information about the current store, language, currency, customer group or whatever else can be request specific.</p>
<p>Now to the test. It is simple as The Beatles. If the <code>DataPoolReader</code> returns an empty set of product IDs matching our bestselling criteria the service should also return an empty array.</p>
<p>Now let’s make this test pass.</p>
<pre><code class="language-php">public function __construct(DataPoolReader $dataPoolReader, ProductJsonService $productJsonService,)
{
    $this-&gt;dataPoolReader = $dataPoolReader;
    $this-&gt;productJsonService = $productJsonService;
}

public function getBestsellers(Context $context) : array
{
    return [];
}</code></pre>
<p>Property declarations are again omitted. Constructor is not doing anything as every good constructor shouldn’t. And the <code>getBestsellers</code> method is returning an empty array so the only test passes.</p>
<p>The next test will not be much more complicated. Simplicity is the key, you know.</p>
<pre><code class="language-php">public function testReturnsArrayOfBestSellingProductData()
{
   $stubProductIds = [$this-&gt;createMock(ProductId::class), $this-&gt;createMock(ProductId::class)];
   $expectedProductData = [['Dummy product A data'], ['Dummy product B data']];

   $this-&gt;mockDataPoolReader-&gt;method('getProductIdsMatchingCriteria')-&gt;willReturn($stubProductIds);
   $this-&gt;stubProductJsonService-&gt;method('get')-&gt;with(stubProductIds)-&gt;willReturn($expectedProductData);

   $this-&gt;assertSame($expectedProductData, $this-&gt;bestsellerService-&gt;getBestsellers($this-&gt;stubContext));
}</code></pre>
<p>So if <code>DataPoolReader</code> returns us a set of product IDs and then those are passed to <code>ProductJsonService</code> it will then return some fake JSON representation for each of them.</p>
<p>This will fail of course as <code>getBestsellers</code> so far always return an empty array so let’s make it pass.</p>
<pre><code class="language-php">public function getBestsellers(Context $context) : array
{
   $productIds = $this-&gt;getBestsellingProductIds($context);

   if ([] === $productIds) {
       return [];
   }

   return $this-&gt;productJsonService-&gt;get($context, ...$productIds);
}

private function getBestsellingProductIds(Context $context) : array
{
   $searchCriteria = SearchCriterionGreaterThan::create('bestselling_score', '10');
   $sortBy = new SortBy(
       AttributeCode::fromString('bestselling_score'),
       SortDirection::create(SortDirection::DESC)
   );
   $rowsPerPage = 12;
   $pageNumber = 0;

   return $this-&gt;dataPoolReader-&gt;getProductIdsMatchingCriteria(
       $searchCriteria,
       $context,
       $sortBy,
       $rowsPerPage,
       $pageNumber
   );
}</code></pre>
<p>No magic here too. First get bestselling product IDs, if there’s none return an empty array in order to satisfy the first test, otherwise pass them to <code>ProductJsonService</code> and return obtained product data to caller.</p>
<p>Actual business logic resides in <code>getBestsellingProductIds</code> utility method.</p>
<p>An important concept here is the search criteria. As the name suggests it is a rule or set of rules for querying products out of the data pool. In our example we use <code>SearchCriterionGreaterThan</code> rule. For the whole set check <code>SearchCriteria</code> interface implementations. One of them deserves a special note. <code>CompositeSearchCriterion</code> allows combining two or more criteria with <code>AND</code> or <code>OR</code> condition. One of those criteria could be <code>CompositeSearchCriterion</code> itself which allows building criteria with unlimited levels of sub-criteria. This however deserves a separate article. To keep our example as simple as possible let’s assume our products have an attribute <code>bestselling_score</code> and we want to pull top 12 with score higher than 10.</p>
<p>The search criteria is then passed to <code>DataPoolReader</code> along with desired sort order, number of results and offset and an array of marching product IDs is returned.</p>
<h3>Request Handler</h3>
<p>This is it about the service class. The next thing that must be implemented is the REST API request handler. When Lizards &amp; Pumpkins gets a REST API request it loops through all registered REST API request handlers trying to find a matching one.</p>
<p>All REST API request handlers must extend the abstract <code>ApiRequestHandler</code> class. Let our first test assure this.</p>
<pre><code class="language-php">final protected function setUp()
{
   $this-&gt;mockBestsellerService = $this-&gt;createMock(BestsellerService::class);
   $this-&gt;stubContextBuilder = $this-&gt;createMock(ContextBuilder::class);

   $this-&gt;requestHandler = new BestsellerApiV1GetRequestHandler(
       $this-&gt;mockBestsellerService,
       $this-&gt;stubContextBuilder
   );

   $this-&gt;stubRequest = $this-&gt;createMock(HttpRequest::class);
}

public function testIsApiRequestHandler()
{
   $this-&gt;assertInstanceOf(ApiRequestHandler::class, $this-&gt;requestHandler);
}</code></pre>
<p>The class will need the service we have just implemented and a <code>ContextBuilder</code>. <code>ContextBuilder</code> is just a simple builder class which in our case will create an instance of a <code>Context</code> from given HTTP request. Once again, the <code>Context</code> is an object that encapsulates information of current store, currency or whatever else can be different from one HTTP request to another.</p>
<p>I also created a stub request here as it will be used by every upcoming test.</p>
<p>Now let’s make it pass.</p>
<pre><code class="language-php">class BestsellerApiV1GetRequestHandler extends ApiRequestHandler
{
   /**
    * @var BestsellerService
    */
   private $bestsellerService;

   /**
    * @var ContextBuilder
    */
   private $contextBuilder;

   public function __construct(BestsellerService $bestsellerService, ContextBuilder $contextBuilder)
   {
       $this-&gt;bestsellerService = $bestselerService;
       $this-&gt;contextBuilder = $contextBuilder;
   }
}</code></pre>
<p>This will force us to implement the <code>canProcess(HttpRequest $request) : bool</code> method of the <code>HttpRequestHandler</code> interface and the abstract <code>getResponse(HttpRequest $request) : HttpResponse</code> template method of the <code>ApiRequestHandler</code> class. The first one must evaluate request and decide if our endpoint is responsible for handling it. The second will return the actual response for the request. But for now we will just place dummies in order to satisfy PHP interpreter.</p>
<pre><code class="language-php">public function canProcess(HttpRequest $request) : bool
{
   return true;
}

final protected function getResponse(HttpRequest $request) : HttpResponse
{
   $body = '';
   $headers = [];

   return GenericHttpResponse::create($body, $headers, HttpResponse::STATUS_OK);
}</code></pre>
<p>The next test must be a most degenerative one. How about we test that the request handler will not be able to process other request types but GET?</p>
<pre><code class="language-php">/**
* @dataProvider nonGetRequestMethodProvider
*/
public function testCanNotProcessNonHttpGetRequestTypes(string $nonGetRequestMethod)
{
   $this-&gt;stubRequest-&gt;method('getMethod')-&gt;willReturn($nonGetRequestMethod);
   $message = sprintf('%s request should NOT be able to be processed', $nonGetRequestMethod);
   $this-&gt;assertFalse($this-&gt;requestHandler-&gt;canProcess($this-&gt;stubRequest), $message);
}

/**
* @return array[]
*/
public function nonGetRequestMethodProvider() : array
{
   return [
       [HttpRequest::METHOD_POST],
       [HttpRequest::METHOD_PUT],
       [HttpRequest::METHOD_DELETE],
       [HttpRequest::METHOD_HEAD],
   ];
}</code></pre>
<p>To make it pass all we have to do is to switch the returned value of our <code>canProcess()</code> dummy from <code>true</code> to <code>false</code>.</p>
<pre><code class="language-php">public function canProcess(HttpRequest $request) : bool
{
   return false;
}</code></pre>
<p>That’s it. Yeah, dumb. But we are only allowed to write a productive code which will make test pass. And this change proves our test to work correctly, so it isn't actually all that dumb. We now need a test which will force us to write the real logic.</p>
<pre><code class="language-php">public function testCanProcessHttpGetRequest()
{
   $this-&gt;stubRequest-&gt;method('getMethod')-&gt;willReturn(HttpRequest::METHOD_GET);
   $message = 'GET request should be able to be processed';
   $this-&gt;assertTrue($this-&gt;requestHandler-&gt;canProcess($this-&gt;stubRequest), $message);
}</code></pre>
<p>To make it pass we just need to check a request type.</p>
<pre><code class="language-php">public function canProcess(HttpRequest $request) : bool
{
   return $request-&gt;getMethod() === HttpRequest::METHOD_GET;
}</code></pre>
<p>Our request handler should only be able to process requests to the given endpoint (let’s name it “bestseller”). Let’s test it also.</p>
<pre><code class="language-php">/**
* @dataProvider nonMatchingRequestPathProvider
*/
public function testCanNotProcessNonMatchingGetRequests(string $nonMatchingRequestPath)
{
   $this-&gt;stubRequest-&gt;method('getMethod')-&gt;willReturn(HttpRequest::METHOD_GET);
   $this-&gt;stubRequest-&gt;method('getPathWithoutWebsitePrefix')-&gt;willReturn($nonMatchingRequestPath);
   $message = sprintf('GET request to "%s" should NOT be able to be processed', $nonMatchingRequestPath);
   $this-&gt;assertFalse($this-&gt;requestHandler-&gt;canProcess($this-&gt;stubRequest), $message);
}

public function nonMatchingRequestPathProvider() : array
{
   return [
       ['/api/'],
       ['/api/foo/'],
       ['/api/bestseller/foo/'],
   ];
}</code></pre>
<p>Quite straightforward. The request path must be <code>/api/bestseller</code> and the rest must be rejected. There is no need to evaluate <code>/api</code> part of request path as this is already done in the <code>ApiRouter</code>. So let’s make this test pass.</p>
<pre><code class="language-php">public function canProcess(HttpRequest $request) : bool
{
   if ($request-&gt;getMethod() !== HttpRequest::METHOD_GET) {
       return false;
   }

   $parts = $this-&gt;getRequestPathParts($request);

   if (count($parts) !== 2 || self::ENDPOINT_NAME !== $parts[1]) {
       return false;
   }

   return true;
}

/**
* @param HttpRequest $request
* @return string[]
*/
private function getRequestPathParts(HttpRequest $request) : array
{
   return explode('/', trim($request-&gt;getPathWithoutWebsitePrefix(), '/'));
}</code></pre>
<p>Now PHPUnit doesn’t like my <code>testCanProcessHttpGetRequest</code> anymore. I’d like to change it into something like this:</p>
<pre><code class="language-php">public function testCanProcessMatchingRequest()
{
   $this-&gt;stubRequest-&gt;method('getMethod')-&gt;willReturn(HttpRequest::METHOD_GET);
   $this-&gt;stubRequest-&gt;method('getPathWithoutWebsitePrefix')-&gt;willReturn('/api/bestseller');

   $this-&gt;assertTrue($this-&gt;requestHandler-&gt;canProcess($this-&gt;stubRequest));
}</code></pre>
<p>All we have to do is to check that after the change all is green again. And it is!</p>
<p>I guess this is it for <code>canProcess()</code> method. Now to <code>getResponse()</code>. Let’s first test that if someone will pass a non matching request it will throw an exception.</p>
<pre><code class="language-php">public function testThrowsAnExceptionDuringAttemptToProcessInvalidRequest()
{
   $this-&gt;expectException(UnableToProcessBestsellerRequestException::class);

   $this-&gt;stubRequest-&gt;method('getMethod')-&gt;willReturn(HttpRequest::METHOD_POST);
   $this-&gt;stubRequest-&gt;method('getPathWithoutWebsitePrefix')-&gt;willReturn('/api/bestseller');

   $this-&gt;requestHandler-&gt;process($this-&gt;stubRequest);
}</code></pre>
<p>Important thing to note here is the method that we are calling on the SUT. <code>process()</code> is a method of the <code>ApiRequestHandler</code> class we extend and it will call <code>getResponse()</code> in its turn. The creation of the exception class is omitted to keep this post focused on the relevant steps.</p>
<p>In order to make this test pass all we have to do is to call the <code>canProcess()</code> method we just created and throw the exception if it returns <code>false</code>.</p>
<pre><code class="language-php">final protected function getResponse(HttpRequest $request) : HttpResponse
{
   if (! $this-&gt;canProcess($request)) {
       throw new UnableToProcessBestsellerRequestException();
   }

   $body = '';
   $headers = [];

   return GenericHttpResponse::create($body, $headers, HttpResponse::STATUS_OK);
}</code></pre>
<p>There’s one last test we are missing. The response body must contain the actual data. The data comes from the service class we have already created so all we have to test is a delegation:</p>
<pre><code class="language-php">public function testDelegatesFetchingProductsToBestsellerService()
{
   $testProductData = ['total' =&gt; 1, 'data' =&gt; ['Dummy data']];

   $this-&gt;stubRequest-&gt;method('getMethod')-&gt;willReturn(HttpRequest::METHOD_GET);
   $this-&gt;stubRequest-&gt;method('getPathWithoutWebsitePrefix')-&gt;willReturn('/api/bestseller');

   $this-&gt;mockBestsellerService-&gt;expects($this-&gt;once())-&gt;method('query')-&gt;willReturn($testProductData);

   $stubContext = $this-&gt;createMock(Context::class);
   $this-&gt;stubContextBuilder-&gt;method('createFromRequest')-&gt;with($this-&gt;stubRequest)-&gt;willReturn($stubContext);

   $response = $this-&gt;requestHandler-&gt;process($this-&gt;stubRequest);

   $this-&gt;assertSame(json_encode($testProductData), $response-&gt;getBody());
   $this-&gt;assertSame(HttpResponse::STATUS_OK, $response-&gt;getStatusCode());
}</code></pre>
<p>And here is the final version of our <code>getResponse()</code> method:</p>
<pre><code class="language-php">final protected function getResponse(HttpRequest $request) : HttpResponse
{
   if (! $this-&gt;canProcess($request)) {
       throw new UnableToProcessBestsellerRequestException();
   }

   $context = $this-&gt;contextBuilder-&gt;createFromRequest($request);
   $data = $this-&gt;bestsellerService-&gt;getBestsellers($context);

   $body = json_encode($data);
   $headers = [];

   return GenericHttpResponse::create($body, $headers, HttpResponse::STATUS_OK);
}</code></pre>
<h3>Factory</h3>
<p>The last class to implement is the <code>BestsellerFactory</code>. It will contain two methods instantiating each of the classes created above plus the REST API endpoint registration. As this is quite a trivial thing and I’m sure you are already bored reading, so I will just print out the <code>BestsellerFactoryTest</code> class entirely:</p>
<pre><code class="language-php">class BestsellerFactoryTest extends \PHPUnit\Framework\TestCase
{
   /**
    * @var BestsellerFactory
    */
   private $factory;

   final protected function setUp()
   {
       $masterFactory = new SampleMasterFactory();
       $masterFactory-&gt;register(new CommonFactory());
       $masterFactory-&gt;register(new RestApiFactory());
       $masterFactory-&gt;register(new UnitTestFactory($this));

       $this-&gt;factory = new BestsellerFactory();

       $masterFactory-&gt;register($this-&gt;factory);
   }

   public function testFactoryInterfaceIsImplemented()
   {
       $this-&gt;assertInstanceOf(Factory::class, $this-&gt;factory);
   }

   public function testFactoryWithCallbackInterfaceIsImplemented()
   {
       $this-&gt;assertInstanceOf(FactoryWithCallback::class, $this-&gt;factory);
   }

   public function testBestsellerApiEndpointIsRegistered()
   {
       $endpointKey = 'get_bestseller';
       $apiVersion = 1;

       $mockApiRequestHandlerLocator = $this-&gt;createMock(ApiRequestHandlerLocator::class);
       $mockApiRequestHandlerLocator-&gt;expects($this-&gt;once())-&gt;method('register')
           -&gt;with($endpointKey, $apiVersion, $this-&gt;isInstanceOf(BestsellerApiV1GetRequestHandler::class));

       $stubMasterFactory = $this-&gt;getMockBuilder(MasterFactory::class)-&gt;setMethods(
           ['register', 'getApiRequestHandlerLocator']
       )-&gt;getMock();
       $stubMasterFactory-&gt;method('getApiRequestHandlerLocator')-&gt;willReturn($mockApiRequestHandlerLocator);

       $this-&gt;factory-&gt;factoryRegistrationCallback($stubMasterFactory);
   }
}</code></pre>
<p>The first test ensures the <code>Factory</code> interface is implemented. Each factory must implement it.</p>
<p>The second test checks that our factory also implements the <code>FactoryWithCallback</code> interface. It will force our factory to have a <code>factoryRegistrationCallback</code> method which is an entry point into a factory.</p>
<p>Last test checks that once this entry point is called and our REST API endpoint is registered.</p>
<p>Here is the productive code:</p>
<pre><code class="language-php">class BestsellerFactory implements Factory, FactoryWithCallback
{
   use FactoryTrait;

   public function factoryRegistrationCallback(MasterFactory $masterFactory)
   {
       $apiVersion = 1;

       /** @var ApiRequestHandlerLocator $apiRequestHandlerLocator */
       $apiRequestHandlerLocator = $masterFactory-&gt;getApiRequestHandlerLocator();
       $apiRequestHandlerLocator-&gt;register(
           'get_bestseller',
           $apiVersion,
           $this-&gt;getMasterFactory()-&gt;createBestsellerApiV1GetRequestHandler()
       );
   }

   public function createBestsellerApiV1GetRequestHandler() : BestsellerApiV1GetRequestHandler
   {
       return new BestsellerApiV1GetRequestHandler(
           $this-&gt;getMasterFactory()-&gt;createBestsellerService(),
           $this-&gt;getMasterFactory()-&gt;createContextBuilder()
       );
   }

   public function createBestsellerService() : BestsellerService
   {
       return new BestsellerService(
           $this-&gt;getMasterFactory()-&gt;createDataPoolReader(),
           $this-&gt;getMasterFactory()-&gt;createProductJsonService()
       );
   }
}</code></pre>
<p>The <code>factoryRegistrationCallback()</code> method receives a master factory. It gets an <code>ApiRequestHandlerLocator</code> from it and registers new REST API endpoint. Very straightforward.</p>
<h3>That’s all. Right?</h3>
<p>No. Let’s write an integration test which will ensure all we just wrote works in real life.</p>
<pre><code class="language-php">class BestsellerApiTest extends AbstractIntegrationTest
{
   public function testEmptyJsonIsReturnedIfNoProductsMatchTheRequest()
   {
       $httpUrl = HttpUrl::fromString('http://example.com/api/bestseller');
       $httpHeaders = HttpHeaders::fromArray(['Accept' =&gt; 'application/vnd.lizards-and-pumpkins.bestseller.v1+json']);
       $httpRequestBody = new HttpRequestBody('');
       $request = HttpRequest::fromParameters(HttpRequest::METHOD_GET, $httpUrl, $httpHeaders, $httpRequestBody);

       $factory = $this-&gt;prepareIntegrationTestMasterFactoryForRequest($request);
       $factory-&gt;register(new BestsellerFactory());

       $implementationSpecificFactory = $this-&gt;getIntegrationTestFactory($factory);

       $website = new InjectableDefaultWebFront($request, $factory, $implementationSpecificFactory);
       $response = $website-&gt;processRequest();

       $this-&gt;assertEquals(json_encode([]), $response-&gt;getBody());
   }

   public function testProductDetailsMatchingRequestAreReturned()
   {
       $httpUrl = HttpUrl::fromString('http://example.com/api/bestseller');
       $httpHeaders = HttpHeaders::fromArray(['Accept' =&gt; 'application/vnd.lizards-and-pumpkins.bestseller.v1+json']);
       $httpRequestBody = new HttpRequestBody('');
       $request = HttpRequest::fromParameters(HttpRequest::METHOD_GET, $httpUrl, $httpHeaders, $httpRequestBody);

       $factory = $this-&gt;prepareIntegrationTestMasterFactoryForRequest($request);
       $factory-&gt;register(new BestsellerFactory());

       $implementationSpecificFactory = $this-&gt;getIntegrationTestFactory($factory);
       $this-&gt;importCatalogFixture($factory);

       $website = new InjectableDefaultWebFront($request, $factory, $implementationSpecificFactory);
       $response = $website-&gt;processRequest();

       $this-&gt;assertGreaterThan(0, count(json_decode($response-&gt;getBody(), true)));
   }
}</code></pre>
<p>As you can see the test class extends <code>AbstractIntegrationTest</code> which is just a set of helper methods shared across integration tests. We will use just 3. I believe their names clearly reveal the intent:</p>
<ul>
<li><code>prepareIntegrationTestMasterFactoryForRequest</code></li>
<li><code>importCatalogFixture</code></li>
<li><code>getIntegrationTestFactory</code></li>
</ul>
<p>So as the <code>importCatalogFixture</code> is only called in the second test the first one deals with an empty catalog so bestsellers REST API call must return an empty JSON and for the second it mustn’t be empty.</p>
<p>This is it. A REST API call is ready. Of course the use case is very simplified, but it all comes down to the logic which the service class uses to select matching products. But as to the REST API endpoint, it is just implementing a service class, a request handler and their factory.</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 19: Worst Magento Practice</title>
      <link>https://www.webguys.de/magento-1/worst-magento-practice</link>
      <guid>magento-1/worst-magento-practice</guid>
      <pubDate>Mon, 19 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>When looking for the biggest problems you can have with your Magento shop, you might think of poor performance,<br />
code quality or updatability. All those are important for a well running shops, but there is one more import problem:</p>
<h2>Security</h2>
<p>Unfortunately, most merchants, most agencies and most developers only take it seriously once they have been affected<br />
by a hacked store directly. This is what can happen:</p>
<ul>
<li>Customer Data stolen</li>
<li>Access Data stolen</li>
<li>Payment Data stolen</li>
<li>Site taken offline</li>
</ul>
<p>Each of those incidents can mean costs high costs - there are statistics saying that the average cost is about 150 $ per<br />
lost or stolen record (see <a href="www.ibm.com/security/data-breach/">www.ibm.com/security/data-breach/</a>).</p>
<h3>Example</h3>
<p>Just one example which happened to me a year ago: during a shop analysis I found a modified core file <strong>lib/Varien/Object.php</strong>.<br />
At the beginning of the file, one line of code was added:</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/exploit.png" alt="exploit"></figure>
<p>This line of code stores all data entered in the checkout process and in the admin area in a single hidden text file which everyone<br />
could download and decode. The exploit was active for <strong>6 months</strong> and contained:</p>
<ul>
<li>5,628 datasets (email address, name, telephone)</li>
<li>1,612 passwords (a part of them could probably have been used for other services like PayPal)</li>
<li>All admin usernames and passwords.</li>
</ul>
<p>The line of code was probably added by using a security hole.</p>
<h3>Counter-Measures: The Obvious</h3>
<p>There are a few things which should be respected by every developer and every shop manager:</p>
<ul>
<li>Keep your Magento updated</li>
<li>At least apply security patches</li>
<li>Keep PHP and other server software up to date</li>
<li>Don't use the default admin username / password</li>
<li>Don't use common usernames and passwords</li>
</ul>
<p>If you are following this advice, you should be safer than 90% of all Magento stores online worldwide which will make<br />
attacking your shop relatively hard and not worthwile for most attackers. Though, if you make any of the following<br />
mistakes, you'll have a problem:</p>
<h2>Top 10 Worst Magento Practices</h2>
<h3>10: Downloadable Code</h3>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/top10.png" alt="top10"></figure>
<p>Don't put your whole code on GitHub - unprotected! Also, if you have your .git directory in your main directory, make sure<br />
noone can access it through the browser - otherwise your whole repository can be downloaded and scanned for security issues.</p>
<h3>Top 9: Downloadable Data</h3>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/top9.png" alt="top9"></figure>
<p>Don't put your database dumps anywhere where they can be accessed by browser, especially not in the main directory.<br />
Not having any links to the file doesn't help much - the files can be found anyways. You don't want anyone to get all your<br />
customer data including password hash.</p>
<h3>Top 8: Unprotected Executables</h3>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/top8.png" alt="top8"></figure>
<p>Those files can be called via the browser by anyone. Depending on the script, calling them can cause importing or exporting<br />
data or doing any other operation, possibly severely messing up your system. For example: if a reindexing script is called<br />
several times in a row, it can block the whole system.</p>
<h3>Top 7: Unprotected Database Credentials</h3>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/top7.png" alt="top7"></figure>
<p>Depending on your server configuration, having your app/etc/local.xml open to access via browser can mean anyone can<br />
have access to your database server. Having database access, they can for example read or delete all your customer data or<br />
create new admin accounts.</p>
<h3>Top 6: Unsecured Admin</h3>
<p>Change your admin URL from /admin/ to anything else. If an attacker can't find the location of your admin area easily,<br />
he will likely go on to another shop which is easier to hack.<br />
The same goes for the /downloader/ URL. You can't change that easily so it's best to <a href="https://www.integer-net.com/why-modules-shouldnt-be-installed-via-magento-connect-manager/">deactivate it completely</a>. </p>
<h3>Top 5: Unsecured Tools</h3>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/top5.png" alt="top5"></figure>
<p>If you have any external tools for your store, make sure they can't be accessed without authentication. The image shows<br />
<strong>Magmi</strong>, a tool for importing data into Magento. I found this tool through a simple Google search and<br />
would have been able to take over the whole system with that. The installed Magmi version allows to install a newer<br />
version of itself up update but doesn't check the source, so I could download any version, add some backdoor code and<br />
upload it via the web interface.</p>
<p>Nearly every external tool can be misued, so please be extra careful about that. I have seen unprotected external tools<br />
which allowed to change appearance of a configurator or access all order data.</p>
<h3>Top 4: Patches not applied</h3>
<p>If you don't install security patches released by Magento or install the latest Magento version, you are vulnerable<br />
to attacks. Example: the <strong>Shoplift bug</strong> which was patched by Magento in Febuary 2015. In April 2016, still<br />
<strong>50,000 out of 250,000</strong> Magento shops were not patched and thus vulnerable to it (Source: <a href="http://byte.nl">byte.nl</a>).<br />
That makes 50,000 easy targets. Don't let your store be one of them.</p>
<p>In order to get to know about security issues, I recommend subscribing to the official <a href="https://magento.com/security/sign-up">Magento Security Alert Registry</a>. </p>
<h3>Top 3: Insecure Modules</h3>
<blockquote class="twitter-tweet" data-lang="de"><p lang="en" dir="ltr">Hmm, do I want to install this module?<br>&quot;return base64_encode(base64_encode(base64_encode(...&quot; <a href="https://twitter.com/hashtag/realmagento?src=hash">#realmagento</a></p>&mdash; Andreas Mautz (@a_v_o_g_t) <a href="https://twitter.com/a_v_o_g_t/status/600948007669014528">20. Mai 2015</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>If you don't know the developer of a module well, you can never know if there isn't some malware or security issue included.<br />
To be sure, you need to do a review of every module you are installing to your store.</p>
<p>Important: If you are serious about security, never install a module which is encrypted (i.e. with Ioncube) or obfuscated.</p>
<h3>Top 2: Database Tools</h3>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/top2.png" alt="top2"></figure>
<p>I found this database management tools during a store review. You should never install database management tools<br />
(like PhpMyAdmin or Adminer) on your store without additionally protecting them as security issues are found in them<br />
regularly.</p>
<p>In this case it was even worse: you don't even need MySQL credential as they have been pre-filled. So you have full<br />
access to the database if you know the (simple) filename which can be guessed or found out otherwise. </p>
<h3>Top 1: Backdoors</h3>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/top1_1.png" alt="top1_1"></figure>
<p>I also found this file in the store review I mentioned above. An input field with the output of the path of the file?<br />
Well, looking into the sourcecode made the purpose of that file clear:</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/top1_2.png" alt="top1_2"></figure>
<p>You can run any server command through this simple web interface. No authentication, no protection, and the power to<br />
do everything on the server you want, depending on the server configuration. Don't do that!</p>
<h2>Conclusion</h2>
<p>Those have been the most hair-rising issues I found in the last years working with Magento. I am sure there are more;<br />
probably every experienced Magento developer can add his/her own examples.</p>
<p>It's hard to impossible to make a Magento store (or any online store) 100% secure, but it's not too difficult to make<br />
it hard enough for attackers so they choose different targets. If you avoid the above mentioned mistakes, you will<br />
have reached that goal.</p>
<p>To check your goals, I suggest using <a href="https://www.magereport.com">MageReport</a> by <a href="http://byte.nl">byte.nl</a>. It will scan your store and tell you<br />
if there are severe security problems left on your store. It will even email you about new issues found if you<br />
register (for free).</p>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161220-worst-magento-practice/magereport.png" alt="magereport"></figure>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 18: Magento 2 for Magento 1 Developers</title>
      <link>https://www.webguys.de/magento-2/magento-2-for-magento1-developers</link>
      <guid>magento-2/magento-2-for-magento1-developers</guid>
      <pubDate>Sun, 18 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>The essential takeaway from this post should be this: if you have strong Magento 1 experience, it should serve you well in Magento 2. Magento 2 tech improves upon it where we could. We have also introduced new concepts and technology which should feel familiar to the modern PHP developer (Composer, dependency injection, etc).</p>
<p>Like Magento 1, the core of a Magento 2 is PHP MVC framework. And just like Magento 1, Magento, Inc. use the architecture and conventions of the underlying framework to create the base digital commerce application, which is available as a free &quot;Community Edition&quot; and a licensed &quot;Enterprise Edition&quot; which adds functionality, scaling capacity, and enhanced core integrations appropriate for enterprise businesses.</p>
<p>Magento 2 offers new features and improvements over Magento 1 in the following areas:</p>
<ul>
<li>Architecture &amp; Conventions</li>
<li>Modularity</li>
<li>Configuration</li>
<li>Customization</li>
<li>Performance &amp; Scaling</li>
<li>Systems Integration</li>
</ul>
<p>As a Magento 1 developers review the Magento 2 filesystem and application, they will have two broad reactions:</p>
<h2>&quot;That Looks Familiar…&quot;</h2>
<ul>
<li>Tons of out-of-the-box commerce and framework functionality</li>
<li>Multisite capability</li>
<li>Internationalization features</li>
<li>Shared themes</li>
<li>Two distinct frontends: one for customers, one for administrators</li>
<li>Modular architecture</li>
<li>MVC with proprietary ORM, powerful blocks &amp; PHP+HTML templates, and thin controllers</li>
<li>User-configurable entities via EAV</li>
<li>Powerful layout XML for composing views</li>
<li>Configuration XML</li>
</ul>
<h2>&quot;…What is That?!?&quot;</h2>
<ul>
<li>Developer documentation</li>
<li>New module registration &amp; activation conventions</li>
<li>Configurable dependency Injection</li>
<li>Plugins, a system for focused customization of core and community functionality; think of every public method as consumable event</li>
<li>Composer, a (the) PHP dependency manager</li>
<li>Tests: unit, integration, and functional</li>
<li>Native CLI component, an extensible tool for performing developer and ops tasks</li>
<li>Service layer, an architecture for defining extension points</li>
<li>UI components, an approach which ensures consistent design and enables efficient client-side rendering</li>
</ul>
<p>I'll go through some of these topics below. If there is interest in other topics, please let me know via comments here, on <a href="https://twitter.com/benmarks">Twitter</a>, or at ben@magento.com.</p>
<h2>Installation</h2>
<p>While Magento 1 had only one official way to install (via archive), there are three methods for installing Magento 2. Most developers will want to create a project via Composer. If you want to contribute to the Magento 2 core via pull request, then you will use <code>git clone</code>. Another option is direct download of the metapackage, but this has limited applicability for developers. The best place for complete information on Magento 2 installation is the <a href="http://devdocs.magento.com/guides/v2.1/install-gde/bk-install-guide.html">DevDocs section on installation</a>. DevDocs is our developer documentation hub, which is open for contribution via pull request.</p>
<p><em>Given that Magento 2 has different system requirements for its Web stack compared to Magento 1 (especially PHP 7), and due to additional technologies used by the core (such as Varnish and Redis), many developers are turning to virtualization for their developer environments. We have an official Docker setup for developers coming out soon (email me if you want early access). There are also several community-maintained Vagrant and Docker projects. I personally use <a href="https://github.com/joshuaswarren/magescotch">MageScotch</a> from <a href="https://twitter.com/JoshuaSWarren">Joshua Warren</a> on my MacBook Pro, and find it to be responsive and easy to maintain.</em></p>
<h2>Architecture &amp; Conventions</h2>
<p>Magento 1 originated in 2006-07, with roots planted before Composer - before testing was in vogue, and before other cultural shifts which have given us modern PHP. In fact, some parts of Magento 1 go all the way back to the days of PHP 4, as there was originally some concern about hosting adoption of PHP 5 at the time (who remembers those days?). Magento 1's architecture was sophisticated when it was born, but these days evinces its age.</p>
<p>Magento 2 architecture seeks to simplify modification and integration by emphasizing true modularity via the following conventions: federated, constructor-based DI which displaces the so-called <code>Mage</code> god class from Magento 1; Composer-based dependency management along with <a href="http://semver.org/spec/v2.0.0.html">semantic versioning</a>; and an injected framework association which allows core concerns such as rendering to be replaced with the smallest footprint possible. This is by no means a complete list, nor does it describe current state for all components. The Magento 2 approach sees us more closely following the <a href="https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)">principles of SOLID</a>, though there is still much refactoring to be done.</p>
<h2>Versioning</h2>
<p>In order to facilitate Composer usage in Magento 2, we employ two version numbers. There is the so-called metapackage version (e.g. 2.1.3) which is used as a marketing version. We use metapackage versions in our communications around functional, security, and bug releases. Underneath this is the so-called platform version (e.g. 102.x.x), for which we follow <a href="http://semver.org/spec/v2.0.0.html">semantic versioning</a>. This approach allows for extensions and customizations to better target core module versions.</p>
<h2>Dependency Management</h2>
<p><a href="http://getcomposer.org/">Composer, Composer, Composer!</a> PHP's dependency manager is an essential tool when working with Magento 2. Core modules and libraries are associated via Composer, and Marketplace - our new extension app store - relies on Composer as well.</p>
<h2>Modularity</h2>
<p>Magento 2 modules have all code, configuration, and theme assets under one main folder. This is different from Magento 1, which confusingly have both PHP classes and configuration underneath a root module folder, but theme assets under a separate path. Another couple of changes from Magento 1 to Magento 2 is the loss of codepools (core, community, local) under <code>./app/code/</code>, and the ability to store modules under <code>./app/code/</code> or in the <code>./vendor/</code> folder.</p>
<p>Regarding the two roots for Magento core code: if you are planning to contribute to the Magento 2 core, you will clone from GitHub and therefore have Magento core modules under <code>./app/code/</code>; if you are doing anything else then you will likely  have Magento core modules under <code>./vendor/</code>. Framework code is similarly variable in location.</p>
<h2>Decoupling &amp; Dependency Injection</h2>
<p>Coupling is a pervasive issue in Magento 1. Coupling comes in many forms and is a bane to both extensibility and testability. For Magento 2 we removed the <code>Mage</code> god class, as its global scope complicates testing and binds code into context. We've almost totally removed direct class instantiation, replacing it instead with constructor-based dependency injection and an object manager which saves us from the insidious implicit coupling that can plague constructor-based DI.</p>
<p><em>It is worth noting that the MVC framework of Magento 2 is almost entirely abstracted away from the application modules via dependency injection. The last main vestige of coupling between application and rendering framework is the abstract block class, and there is work underway to remove this dependency. There are also plans to move away from model-based CRUD in a future release of Magento 2; see our lead architect's <a href="http://magento.stackexchange.com/a/150626/5">explanation about this</a> on Magento SE.</em></p>
<h2>Configuration</h2>
<p>Like its forebear, Magento 2 is a primarily configuration-based system (as opposed to convention-based), and the mechanism of configuration is still XML. However, there are two major improvements to Magento 2's config XML: use-based organization in multiple files and schema definitions (XSD). In Magento 1 the main module configuration file (<code>config.xml</code>) is a wide-open DOM for mixed multiple concerns, such as store-scoped configuration data, class name mapping, route configuration, module-specific settings, email settings. This unrestricted, unstructured approach has been a constant source of confusion while learning and cause of errors while developing. A further complication is that the execution-scope version of some parts of the DOM is derived from both the filesystem and the database!</p>
<p>In Magento 2 we still make ample use of XML for configuration, but to improve usability and reduce developer errors we separate XML into different files which indicate the XML's usage. For example, <code>acl.xml</code> contains permission node configuration only. Try to add a functionally-unrelated node to your module's <code>acl.xml</code> and your IDE should indicate the error. Another good example is <code>config.xml</code>, which now only contains default store-scoped values, all the more important given that these values are merged with DB-stored configurations.</p>
<p>Another dimension of improvement in config XML organization is the separation of config XML file types by area. This can typically be seen in route, event, and DI configuration. While the associated XSDs are the same, the directives in these files can be scoped according to functional areas, typically global (<code>etc/*.xml</code>), customer/public (<code>etc/frontend/*.xml</code>), and admin (<code>etc/adminhtml/*.xml</code>) - note that other areas exist and additional areas can be defined. This removes another level of hierarchy from the DOM structure. An additional benefit of this approach is more granular caching and in-memory performance of these structures.</p>
<h2>Layout XML</h2>
<p>Layout XML has evolved in Magento 2 to better serve its purpose. In addition to being covered by an XSD, it is more intuitive to understand what each element does. In Magento 1, the concept of a page is just another block, but in Magento 2 pages are now a separate element with better encapsulation of the things which are relevant to a page, such as how many columns there are. Structural blocks such as the left and right columns are similarly differentiated from functional blocks. Going up a level, the storage of these directives is now broken out by layout handle per file, which improves both overall readability and ease of determining what a given module is doing to the view in a given view scope.</p>
<h2>Customization</h2>
<p>An essential feature of commerce applications is the ability to be customized based on merchant requirements. One of the most exciting things about Magento 2 are the many customization mechanisms. Just like Magento 1, Magento 2 has events which the observer system which can consume in multiple scopes. Replacing one class with another still exists as well. In Magento 1 these are called rewrites and are declared in configuration XML. In Magento 2 these are called preferences and are effected via the DI system. What makes Magento 2 more extendable is the plugin system. Magento 2's plugin architecture is inspired by Aspect-Oriented Programming. It allows to declare in <code>di.xml</code> some class and method which will be executed before, after, or around <em>any public Magento method</em>. The complete syntax and explanation for Magento 2 plugins is <a href="http://devdocs.magento.com/guides/v2.1/extension-dev-guide/plugins.html">available on DevDocs</a>.</p>
<p>The benefit of plugins cannot be understated: they facilitate focused addition to or (if necessary) override of application code (whether local, core, or third-party). Magento 1 developers who have ever wanted an event where one did not exist have experienced the problem that plugins solve - especially if a class rewrite was out of the question.</p>
<h2>Testability</h2>
<p>Thanks to the tireless work of luminaries like Sebastian Bergmann, Chris Hartjes, and others - along with the ever-sophisticating trend of PHP development - software testing is now de rigueur in PHP. Core testability is therefore an essential concern for Magento 2 (a reality which simply doesn't exist for Magento 1). Our initial and current architectural approach is driven by the need to make classes and components testable, and you can find multiple test types in the Magento 2 core: unit, integration, and functional. Given that fact, covering your custom code with tests is not really an option anymore. While testing may be new to many Magento 1 developers, fortunately there are a number of resources to learn PHP testing in general. There are also Magento 2-specific testing resources; Vinai Kopp's Mage2katas series is a great place to start.</p>
<h2>Contribution</h2>
<p>Magento 2 is the ability to contribute via pull request, which was not possible in Magento 1. This took considerable retooling of how we engineer, but it has been an important part of the Magento 2 story. 2.0 was released with numerous PRs as part of mainline, and we have many, many more waiting review &amp; merging. It is not ideal to make PRs sit in queue for so long, so we are currently putting substantial effort into both our ability to process PRs and our ability to release more frequently, so that contributions can make it into Magento 2 more quickly.</p>
<h2>Conclusion</h2>
<p>There is a lot more to the Magento 2 story. As mentioned earlier in the post, I am eager to hear which of the topics listed (or even additional topics) you might like me or my colleagues to write about. I hope you will take a look at Magento 2 and join this next phase in Magento's evolution.</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 17: Better organize your dependencies and even your source code with composer</title>
      <link>https://www.webguys.de/magento-2/better-organize-your-dependencies-and-even-your-source-code-with-composer</link>
      <guid>magento-2/better-organize-your-dependencies-and-even-your-source-code-with-composer</guid>
      <pubDate>Sat, 17 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Organizing code isn't so easy. Sometimes it is even a warrior's journey.<br />
<a href="http://getcomposer.org/">Composer</a> and <a href="https://git-scm.com/">git</a> are tools that every developer knows for years now.</p>
<p>But do you really know how to use composer? Let me give you some tips and tricks to help you to organize the source code of your projects.</p>
<p><em>Note: I will consider that you already have <a href="http://getcomposer.org/">composer</a> and <a href="https://git-scm.com/">git</a> installed on your machine.</em></p>
<p>Composer allows you to manage your dependencies. We often use a git repository behind each dependency. Also, if your dependency is packaged, it becomes possible to use the archive ball instead of the sources.</p>
<p>Git is there to help you organizing your developments. Yes! It is not only a versioning tool. You can use it like me, every day, as a powerful tool, helping me organizing my commits and my git trees and branches.<br />
And composer doesn't work without git (in fact it does, but I can't recommend it during development).</p>
<p>By using both of them at the same time we can go so far in industrialization and code maintainability.</p>
<p>So, following are few things I wanted to share.</p>
<h2>Managing versions, even during development</h2>
<p>When you ask composer to include a dependency without specifying its version, it will try to install the latest available version. In other words the last tag in the git repository, or maybe not the last, but always the higher.</p>
<p>You can indicate a multitude of methods of resolution depending on how you name the required version.</p>
<p>If your dependency doesn't have any tag (which means no version) then you can use the <code>@dev</code> version which indicates to take the last commit in the main branch.</p>
<p>If you want to use a specific state of your project, using the commit hash, you can do it by specyfiyng it in the <code>require</code> node in the <code>composer.json</code> like <code>"jacques/foo": "dev-master#5538b1e"</code>.<br />
In that case the version <code>5538b1e</code> of the <code>master</code> branch will be used.</p>
<p>I prefer to use the <code>dev-master</code> notation for our internal modules because we know that their master branch is always stable.<br />
But be careful! Never use an unstable version (like <code>@dev</code> or <code>dev-branchname</code>) outside your project. So, no unstable dependencies into a module which will be distributed.<br />
You can do it but the <code>composer.json</code> should also contain any unstable dependencies required by your dependencies! Does it make sense to you?</p>
<p>In other words, if you require a dependency <code>A</code> which has itself a dependency to <code>B</code> version <code>@dev</code>, <code>composer</code> will stop you as it's impossible to install an unstable package.<br />
If you still want to do it, you'll also have to require the <code>B</code> package version <code>@dev</code>. In that case, it's ok, because all de development releases (or unstable versions) are specified in your own <code>composer.json</code>.</p>
<p>If your tag is name <code>v1.0.1</code> then the version to use in your <code>composer.json</code> will be <code>1.0.1</code>. Composer making itself the prefix removal.</p>
<p>You can use the tilde <code>~</code> sign to specify that only the minimum minor version can change.<br />
As examples:</p>
<ul>
<li><code>~1.2.3</code> as <code>&gt;=1.2.3 AND &lt;1.3.0</code>.</li>
<li><code>~1.2</code> as <code>&gt;=1.2.0 AND &lt;2.0.0</code>. In that case the <code>2.0.0-beta1</code> version which is considered as <code>&lt;2.0.0</code> won't be used because the major version is updated. Which is forbidden by the tilde &quot;rules&quot;.</li>
</ul>
<p>In other case it's the <code>^</code> symbol which allows you to install any version before any BC break.<br />
Example:</p>
<ul>
<li><code>^1.2.3</code> as <code>&gt;=1.2.3 AND &lt;2.0.0</code></li>
</ul>
<p>You should always prefer to use a strict version number to define your dependencies. You'll avoid a lot of headaches.<br />
By fixing the commit hash or directly with a strict version number, as you prefer.</p>
<p>In your libraries or modules, prefer <code>^</code> because the theory says that still a version doesn't have any BC break, it's compatible with your code.<br />
In practice it's not always the case. But you can have hope and chance! And maybe all your dependencies follow the semantic versioning rules. Like you of course!</p>
<h2>Composer alias to fix conflicts or only to change the version of a dependency</h2>
<p>Composer is a powerful tool. But it doesn't know how to resolve conflicts between two versions of the same package.</p>
<p>Let us take the example of the world wide known library <code>jacques/yolo</code>, which has <a href="https://hoa-project.net/En/Literature/Hack/Zombie.html">Hoa\Zombie</a> version <code>^2</code> (same as <code>~2.0</code>, <code>&gt;=2.0,&lt;3.0</code>) as a dependency.</p>
<p>But what if you really need a version greater than <code>3.0</code> in your project?</p>
<blockquote>
<p>Houston, we've had a problem.</p>
</blockquote>
<p>Should composer use the version <code>2</code> (required by a dependency) or <code>3</code> (required by you) of your dependency?</p>
<p>If you require the version <code>3</code> (the latest version available) in your own <code>composer.json</code> you'll get an error:</p>
<p><pre class="highlight"><code>$ composer require hoa/zombie
Using version 3.16.01.11 for hoa/zombie
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - jacques/yolo dev-master requires hoa/zombie 2.* -&gt; satisfiable by hoa/zombie[2.14.09.17, &hellip;] but these conflict with your requirements or minimum-stability.

Installation failed, reverting ./composer.json to its original content.</code></pre></p>
<p>As you can see composer wants to use the version <code>3.*</code> but it can't because the package <code>jacques/yolo</code> uses already that package, but in its version <code>2.*</code>.</p>
<p>Don't worry. We have aliases!</p>
<p>Let's update our <code>composer.json</code> manually. We have to add the dependency by ourselves because the previous command failed and the changes have been reverted.</p>
<p>In the node <code>require</code> of your <code>composer.json</code>, add the following dependency: <code>"hoa/zombie": "3.16.01.11 as 2.14.09.17"</code>.<br />
We have to be strict with the version because that's how aliases work. We just told composer to use the version <code>3.16.01.11</code> instead of the <code>2.14.09.17</code>.</p>
<p>So now our dependency to <code>3.*</code> is downloaded and installed because the conflict is solved!<br />
But be careful, the dependency in <code>jacques/yolo</code> is <code>2.*</code> and not <code>2.14.09.17</code>. So… maybe you'll have to do it again.</p>
<p>But if the version 3 is on its way, there are not so much possibilities that the version 2 will evolved a lot. Kind of safe.</p>
<p>We had this problem with <a href="https://github.com/magento/magento2/issues/2476">composer and its own version in Magento 2</a>. True story bro.<br />
So, it's possible to upgrade some dependencies of Magento 2. And because they fixed every version, it should be easy and &quot;safe&quot;.</p>
<p>Aliases can fix some issues but it is better to avoid them. Like always, if you can do it without getting around it, do it!<br />
They are like rewrites in Magento 1, it is better without.</p>
<h2>Repo path</h2>
<p>If you want to manage a module, like a theme, aside your <code>app</code> folder you can do it using the <code>path</code> repository of composer.</p>
<p>Ok, given the following tree:</p>
<p><pre class="highlight"><code>.
├── .git/
├── apps/
│&nbsp;&nbsp; └── magento2/
│   &nbsp;&nbsp;&nbsp; ├── composer.json
│   &nbsp;&nbsp;&nbsp; └── &hellip;
└── modules/
    └── Mbiz_Price/
    &nbsp;&nbsp;&nbsp; ├── composer.json
    &nbsp;&nbsp;&nbsp; └── &hellip;</code></pre></p>
<p>In our Magento 2 we want to install our <code>Mbiz_Price</code> module.<br />
But this module is outside the <code>apps/magento2</code> folder. It can be a theme, or anything else.</p>
<p>It has its own <code>composer.json</code>, with the <code>name</code> (<code>jacques/mbiz_price</code>) of the package.</p>
<p>Let's go in our <code>magento2</code> folder and run some commands to require our <code>Mbiz_Price</code> module:</p>
<p><pre class="highlight"><code>$ cd apps/magento2
[apps/magento2/] $ composer require jacques/mbiz_price

  [InvalidArgumentException]
  Could not find package jacques/mbiz_price at any version for your minimum-stability (stable). Check the package spelling or your minimum-stability</code></pre></p>
<p>Hum… Of course, how could composer be aware of our local package?</p>
<p>We have to update our <code>composer.json</code> (in <code>apps/magento2</code>): (very simple version, for the purpose of the article)</p>
<p><pre class="highlight"><code>{
    &quot;repositories&quot;: [
        {
            &quot;type&quot;: &quot;path&quot;,
            &quot;url&quot;: &quot;../../modules/Mbiz_Price&quot;,
            &quot;options&quot;: {
                &quot;symlink&quot;: true
            }
        }
    ],
    &quot;require&quot;: {
        &quot;jacques/mbiz_price&quot;: &quot;*&quot;
    }
}</code></pre></p>
<p>Now we run the update of composer:</p>
<p><pre class="highlight"><code>[apps/magento2/] $ composer update jacques/mbiz_price
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing jacques/mbiz_price (dev-master)
    Symlinked from ../../modules/Mbiz_Price

Writing lock file
Generating autoload files</code></pre></p>
<p>But wait! How do we define a version for <code>Mbiz_Price</code>? Why <code>*@dev</code>?</p>
<p>As you can see in the initial tree, we are in a git repository, and this repository is on a branch (we don't really know which one) and our module is tracked by this repository.</p>
<p>How to translate &quot;our repository is on a branch, but we don't know which one&quot; to a version number?</p>
<p><code>*@dev</code> makes sense isn't it!? <code>*</code> stands for &quot;any version&quot; and <code>@dev</code> stands for <code>development branch</code> (which is equal to<code>current branch</code>).<br />
But… You'll probably have few questions, like this one:</p>
<p><strong>If we update the content of <code>Mbiz_Price/</code>, do we have to run <code>composer update</code> again? Even after few commits?</strong></p>
<p>No. You don't have to.<br />
You can notice the <code>"symlink": true</code> in the <code>options</code> node. This option allows you to forget about the content of your package.</p>
<p>If you don't want to symlink your package, you'll have one big problem: your package won't be able to change its version. It means that even if you add a lot of content in your module, then you commit them, then you run a <code>composer update</code> (in order to update the module in your <code>magento2</code> project), you'll get something like this:</p>
<p><pre class="highlight"><code>[apps/magento2/] $ composer update jacques/mbiz_price
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files</code></pre></p>
<p>Using the <code>symlink</code> option is safer.</p>
<p>The only moment the package will be updated is when you checkout another branch. In that case, because the branch name is important to composer, the hash will be updated.</p>
<p>So, one important thing to avoid updating the <code>composer.lock</code> too much times: always run a global <code>composer update</code> on the same branch (like <code>develop</code>).<br />
If you are on a specific branch (like <code>feature/my-stuff</code>), always update only the packages you need, like <code>composer update jacques/yolo</code>. Be specific is the best thing to do.</p>
<p>If you don't follow any rules, you'll have some strange behaviors with composer, but nothing really hard to solve, don't be afraid.</p>
<h2>Metapackages</h2>
<p>When you have a lot of dependencies you use in all your projects (or most of them…) you can probably create a metapackage!</p>
<p>A metapackage is like a shortcut, or a Pandora box (depending on what you have in it). You require the metapackage and the metapackage requires other dependencies, which require other dependencies… inception!</p>
<p>It's only a composer.json, nothing more, nothing less.</p>
<p>It is useless to add some <code>require-dev</code> (see below) in your metapackage because composer won't install them in your project.</p>
<p>Be careful, never use an unstable version in your metapackages. And it's better to fix the versions of your dependencies (like <code>1.0.1</code>), unless you know that your code is well developed.<br />
In that case you can have a version like <code>^1.0</code>, but not <code>~1.0</code>. Because the caret is considered as stable, the tilde is considered as dev stability.</p>
<p>We use the caret symbol in our <a href="https://github.com/monsieurbiz/Mbiz_MetaPackage/blob/master/composer.json"><code>Mbiz_MetaPackage</code></a>.<br />
We fixed the versions of our dependencies mostly to <code>^0.1</code> (which means <code>&gt;=0.1.0,&lt;1.0</code>).</p>
<p>But we don't use a specific version for our metapackage because we want to specify the right version on each project.<br />
So, to require the metapackage we run the following command:</p>
<p><pre class="highlight"><code>[apps/magento2/] $ composer require monsieurbiz/mbiz_metapackage:dev-master#8af4f6d
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing monsieurbiz/mbiz_setup (0.1.1)
    Loading from cache
  &hellip;
Writing lock file
Generating autoload files</code></pre></p>
<p>And we are done!</p>
<p>We do that because the metapackage can change. It always has more or less dependencies, depending on our developments.<br />
Using the caret in the metapackage dependencies allows us to have a more stable version of them. But specifying the hash of the metapackage gives us a safety check: we won't get a new dependency magically after running a <code>composer update</code>!</p>
<p>Unlike other dependencies, the metapackage project is not installed in your <code>vendor/</code> directory.</p>
<p>Have a look to the &quot;Satis&quot; part below if you want to know how to host the references of your private packages.<br />
Why the power of composer should be available only for public repositories?</p>
<h2>require-dev: the toolbox!</h2>
<p>It is possible to use the <code>require-dev</code> node, instead of the main <code>require</code> node.</p>
<p>In that case, if your dependencies are in <code>require-dev</code>, they will be installed only if you don't speficy the <code>--no-dev</code> when you run any composer command.</p>
<p>It works well on Magento 2 because a dependency is really a dependency in the <code>vendor/</code> directory.<br />
It doesn't work well on Magento 1 since it uses a lot of symlinks in your <code>app/</code> folder.</p>
<p>So, let's use the package <code>jacquesbh/eater</code>, which is really simple. It's like the <code>Varien_Object</code> in Magento 1, I'm sure you know it.</p>
<p>But we only want this package during development, not in production.</p>
<p><pre class="highlight"><code>[apps/magento2/] $ composer require --dev jacquesbh/eater
Using version ^2.0 for jacquesbh/eater
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Writing lock file
Generating autoload files</code></pre></p>
<p>You can require(-dev) a lot of tools like <a href="http://symfony.com/doc/current/components/debug.html"><code>symfony/debug</code></a> or <a href="http://symfony.com/doc/current/components/var_dumper.html"><code>symfony/var-dumper</code></a>!</p>
<p>In Magento 2 it is possible to add a lot of commands to the <code>magento</code> binary. Don't hesitate to create your own commands available only for the developers and not in production.<br />
It can save a lot of time to your team. Human operations are never safe!</p>
<h2>Scripts</h2>
<p>Composer can run some scripts when some events appear, like &quot;a package just got installed&quot; (<code>post-package-install</code> event).</p>
<p>So, if you want to run some commands, like running a shell command (<code>chmod +x bin/magento</code> as example), you can easily do it in your <code>composer.json</code>:</p>
<p><pre class="highlight"><code>{
    &hellip;
    &quot;scripts&quot;: {
        &quot;post-update-cmd&quot;: [
            &quot;chmod +x bin/magento&quot;
        ]
    }
}</code></pre></p>
<p>You can, by example, execute the run of your test suites after updating your dependencies.  </p>
<p>You can also add some commands to composer, using them like <code>composer yolo</code>. Here is howto:</p>
<p><code>composer.json</code>:</p>
<p><pre class="highlight"><code>{
    &hellip;
    &quot;scripts&quot;: {
        &quot;yolo&quot;: &quot;echo yolo&quot;
    }
}</code></pre></p>
<p>Then:</p>
<p><pre class="highlight"><code>[apps/magento2/] $ composer yolo
&gt; echo yolo
yolo</code></pre></p>
<p>It can be perfect to run some commands like <code>phpunit</code>, or…<br />
If you alias (bash alias) <code>composer</code> to <code>c</code> (like I do), and you add a command <code>mage</code>, you can do that kind of things: <code>c di</code>.</p>
<p>Which will run <code>./bin/magento setup:di:compile</code> as example.</p>
<p>Or simply… <code>c mage setup:di:compile</code> or <code>c mage my:command</code> to run any command using the <code>magento</code> binary.</p>
<p><pre class="highlight"><code>{
    &hellip;
    &quot;scripts&quot;: {
        &quot;mage&quot;: &quot;php ./bin/magento&quot;
    }
}</code></pre></p>
<p>So now, imagine you add your own commands using the <code>require-dev</code> and run them with ease directly using composer! So perfect.</p>
<p><pre class="highlight"><code>c mage dev:my:command</code></pre></p>
<p>Tip about <code>symfony/console</code>: <code>./bin/magento set:di:com</code> is the same as <code>./bin/magento setup:di:compile</code>! Shortcuts are everywhere!</p>
<h2>Packagist or satis ?</h2>
<p>Packagist is the main composer repository. You can add your public packages directly by registering them on <a href="https://packagist.org">https://packagist.org</a>.</p>
<p>You can get your own version of <a href="https://github.com/composer/packagist">packagist by cloning/forking it</a>. But it can be kind of a huge application if you have only few packages.</p>
<p>What about your own and simple composer repository?</p>
<p>Do you know satis?</p>
<p>Satis is a simple composer repository. It is really <a href="https://github.com/composer/satis">easy to install</a>.</p>
<p>Firegento has <a href="https://packages.firegento.com/">its own satis running</a>. You can use it for a lot of Magento 1 modules.<br />
To use it, you can add it to your project's <code>composer.json</code> by using this command:</p>
<p><pre class="highlight"><code>composer config repositories.firegento composer https://packages.firegento.com</code></pre></p>
<p>Or globally by using this command:</p>
<p><pre class="highlight"><code>composer config -g repositories.firegento composer https://packages.firegento.com</code></pre></p>
<p>Last thing about composer repositories: <a href="https://github.com/Seldaek">Jordi Boggiano</a> created <a href="https://toranproxy.com/">Toran Proxy</a>: it is a private and hosted composer repository. I can recommend it if you don't want to maintain your own packagist or satis instance(s). It can be used as a backup repository in case github goes down too! Very helpful if you have a lot of projects depending on github.</p>
<h2>A project template?</h2>
<p>I hope you installed Magento 2 using composer! If you did it, you probably remember this command line:</p>
<p><pre class="highlight"><code>composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition my-project/</code></pre></p>
<p>Now, we take a look at the <code>composer.json</code> of the <code>magento/project-community-edition</code> package:</p>
<p><pre class="highlight"><code>{
    &quot;name&quot;: &quot;magento/project-community-edition&quot;,
    &quot;description&quot;: &quot;eCommerce Platform for Growth (Community Edition)&quot;,
    &quot;type&quot;: &quot;project&quot;,
    &quot;version&quot;: &quot;2.1.2&quot;,
    &quot;license&quot;: [
        &quot;OSL-3.0&quot;,
        &quot;AFL-3.0&quot;
    ],
    &quot;require&quot;: {
        &quot;magento/product-community-edition&quot;: &quot;2.1.2&quot;,
        &quot;composer/composer&quot;: &quot;@alpha&quot;
    },
    &quot;require-dev&quot;: {
        &quot;phpunit/phpunit&quot;: &quot;4.1.0&quot;,
        &quot;squizlabs/php_codesniffer&quot;: &quot;1.5.3&quot;,
        &quot;phpmd/phpmd&quot;: &quot;@stable&quot;,
        &quot;pdepend/pdepend&quot;: &quot;2.2.2&quot;,
        &quot;fabpot/php-cs-fixer&quot;: &quot;~1.2&quot;,
        &quot;lusitanian/oauth&quot;: &quot;~0.3 &lt;=0.7.0&quot;,
        &quot;sebastian/phpcpd&quot;: &quot;2.0.0&quot;
    },
    &quot;config&quot;: {
        &quot;use-include-path&quot;: true
    },
    &quot;autoload&quot;: {
        &quot;psr-4&quot;: {
            &quot;Magento\\Framework\\&quot;: &quot;lib/internal/Magento/Framework/&quot;,
            &quot;Magento\\Setup\\&quot;: &quot;setup/src/Magento/Setup/&quot;,
            &quot;Magento\\&quot;: &quot;app/code/Magento/&quot;
        },
        &quot;psr-0&quot;: {
            &quot;&quot;: &quot;app/code/&quot;
        },
        &quot;files&quot;: [
            &quot;app/etc/NonComposerComponentRegistration.php&quot;
        ]
    },
    &quot;autoload-dev&quot;: {
        &quot;psr-4&quot;: {
            &quot;Magento\\Sniffs\\&quot;: &quot;dev/tests/static/framework/Magento/Sniffs/&quot;,
            &quot;Magento\\Tools\\&quot;: &quot;dev/tools/Magento/Tools/&quot;,
            &quot;Magento\\Tools\\Sanity\\&quot;: &quot;dev/build/publication/sanity/Magento/Tools/Sanity/&quot;,
            &quot;Magento\\TestFramework\\Inspection\\&quot;: &quot;dev/tests/static/framework/Magento/TestFramework/Inspection/&quot;,
            &quot;Magento\\TestFramework\\Utility\\&quot;: &quot;dev/tests/static/framework/Magento/TestFramework/Utility/&quot;
        }
    },
    &quot;minimum-stability&quot;: &quot;alpha&quot;,
    &quot;prefer-stable&quot;: true,
    &quot;repositories&quot;: [
        {
            &quot;type&quot;: &quot;composer&quot;,
            &quot;url&quot;: &quot;https://repo.magento.com/&quot;
        }
    ],
    &quot;extra&quot;: {
        &quot;magento-force&quot;: &quot;override&quot;
    }
}</code></pre></p>
<p>The package type is <code>project</code>. Which means that we can use it with the <code>create-project</code> command.<br />
That command copies everything which is in the project in your project directory then runs a simple <code>composer install</code>.</p>
<p>And because it runs a <code>composer install</code>, we can see that it requires a <code>magento/product-community-edition</code>: a metapackage.</p>
<p>This project defines the Magento repository, it forces the stability of the packages to <code>alpha</code> (not great… but because of composer in <code>@alpha</code>, but it's not required because the stability is forced when you use the <code>@</code> operator in a version).<br />
It adds some autoloads to composer and some <code>require-dev</code> packages. These packages will be installed because they will be in your main <code>composer.json</code> (because it's copied from the <code>project</code>).<br />
It means that by default you'll have <code>phpunit</code> and so on, installed on your machine.</p>
<p>Oh, and by the way, if you can't do the <code>composer create-project</code> command because your platform doesn't have all the system dependencies (like some PHP extensions) but you'll run your project on a VM so you don't want them to be installed, you can use the <code>--ignore-platform-reqs</code> as an argument of your command:</p>
<p><pre class="highlight"><code>composer create-project --ignore-platform-reqs --repository-url=https://repo.magento.com/ magento/project-community-edition my-project/</code></pre></p>
<p>Much better!</p>
<p>Here is the <code>composer.json</code> of the metapackage <code>magento/product-community-edition</code>: (with some <code>…</code> to avoid to paste it entirely)</p>
<p><pre class="highlight"><code>{
  &quot;name&quot;: &quot;magento/product-community-edition&quot;,
  &quot;description&quot;: &quot;eCommerce Platform for Growth (Community Edition)&quot;,
  &quot;version&quot;: &quot;2.1.2&quot;,
  &quot;license&quot;: [
    &quot;OSL-3.0&quot;,
    &quot;AFL-3.0&quot;
  ],
  &quot;type&quot;: &quot;metapackage&quot;,
  &quot;require&quot;: {
    &quot;magento/magento2-base&quot;: &quot;2.1.2&quot;,
    &quot;php&quot;: &quot;~5.6.5|7.0.2|7.0.4|~7.0.6&quot;,
    &quot;zendframework/zend-stdlib&quot;: &quot;~2.4.6&quot;,
    &quot;zendframework/zend-code&quot;: &quot;~2.4.6&quot;,
    &quot;&hellip;&quot;: &quot;&hellip;&quot;,
    &quot;ext-simplexml&quot;: &quot;*&quot;,
    &quot;ext-mcrypt&quot;: &quot;*&quot;,
    &quot;ext-hash&quot;: &quot;*&quot;,
    &quot;ext-curl&quot;: &quot;*&quot;,
    &quot;&hellip;&quot;: &quot;&hellip;&quot;,
    &quot;magento/module-marketplace&quot;: &quot;100.1.1&quot;,
    &quot;magento/module-admin-notification&quot;: &quot;100.1.1&quot;,
    &quot;&hellip;&quot;: &quot;&hellip;&quot;,
    &quot;magento/module-catalog&quot;: &quot;101.0.2&quot;,
    &quot;magento/module-wishlist&quot;: &quot;100.1.2&quot;,
    &quot;&hellip;&quot;: &quot;&hellip;&quot;,
    &quot;magento/theme-adminhtml-backend&quot;: &quot;100.1.1&quot;,
    &quot;magento/theme-frontend-blank&quot;: &quot;100.1.1&quot;,
    &quot;magento/theme-frontend-luma&quot;: &quot;100.1.1&quot;,
    &quot;magento/language-de_de&quot;: &quot;100.1.0&quot;,
    &quot;magento/language-en_us&quot;: &quot;100.1.0&quot;,
    &quot;magento/language-es_es&quot;: &quot;100.1.0&quot;,
    &quot;&hellip;&quot;: &quot;&hellip;&quot;,
    &quot;magento/framework&quot;: &quot;100.1.2&quot;
  }
}</code></pre></p>
<p>That metapackage requires every dependencies of Magento 2.</p>
<p>So, to be clear with the &quot;project&quot; thing:</p>
<p>You can create a package with the <code>project</code> type.<br />
This package is a project &quot;template&quot;. Everything in it will be copied to your new project directory.<br />
It can require some packages and metapackages in order to init the dependencies of your new project.</p>
<p>Creating a composer project is really a time saver. With it you can initiate a lot of projects, depending on what you want to create.<br />
You can store all your &quot;projects&quot; on a satis repository or even on Packagist.</p>
<h2>Conclusion</h2>
<p>It was a mega big article because composer is a great great tool.</p>
<p>If you use it great, you can reduce the time you spend on maintainance. And you can really improve your developments.</p>
<p>By creating some metapackages, some projects, using the power of the versions to install whatever you want to.</p>
<p>And a last tip… Please bookmark <a href="http://composer.json.jolicode.com/">http://composer.json.jolicode.com/</a>!</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 16: What Happens When a Community Works Together</title>
      <link>https://www.webguys.de/magento-1/what-happens-when-a-community-works-together</link>
      <guid>magento-1/what-happens-when-a-community-works-together</guid>
      <pubDate>Fri, 16 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>If you were at Meet Magento New York in 2015, you may have heard Ben Marks share that there is no problem that community cannot solve.</p>
<blockquote class="twitter-tweet" data-lang="en">
    <p lang="en" dir="ltr">&quot;There is no problem that community cannot solve.&quot; <a href="https://twitter.com/benmarks">@benmarks</a> <a href="https://twitter.com/hashtag/mn15ny?src=hash">#mn15ny</a> <a href="http://t.co/twNNQkrKwn">pic.twitter.com/twNNQkrKwn</a></p>&mdash; Sherrie Rohde (@sherrierohde) <a href="https://twitter.com/sherrierohde/status/646085473945944064">September 21, 2015</a>
</blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I couldn’t agree more, and I think even beyond that, there is simply no limit to what we can accomplish when we work together.</p>
<h2>What does this look like for our community?</h2>
<p>In 2015, the Magento community:</p>
<ul>
<li>presented over 500 talks on Magento or at Magento-centric events,</li>
<li>produced over 100 podcast episodes centered around Magento,</li>
<li>organized conferences and meetups in over 24 different countries,</li>
<li>wrote three books about Magento,</li>
<li>contributed to official product documentation and core code,</li>
<li>wrote countless blog posts to raise awareness and education, and</li>
<li>answered thousands of questions on both StackExchange and the Magento Forums.</li>
</ul>
<p><strong>While 2016 is not quite over, it's still clear this community is running forward at a rapid rate!</strong></p>
<p>So far in 2016, the Magento community has:</p>
<ul>
<li>presented over 725 talks on Magento or at Magento-centric events,</li>
<li>produced over 100 podcast episodes centered around Magento,</li>
<li>organized conferences and meetups in over 34 different countries,</li>
<li>written seven books about Magento,</li>
<li>contributed to official product documentation and core code,</li>
<li>written over 1,000 blog posts to raise awareness and education, and</li>
<li>answered thousands of questions on both StackExchange and the Magento Forums.</li>
</ul>
<p>In addition to everything above, your endless efforts of testing, feedback, pull requests, and reported issues have all helped improve our product to make it better meet your needs as well as the needs of your customers and our broader community. (Yay, open source!)</p>
<h2>How can we keep working together?</h2>
<p>Whether you’re new to the Magento Community (hi!) or a veteran looking for more ways to get involved, here’s a few ideas:</p>
<ul>
<li>Answer, or ask, questions on the <a href="https://community.magento.com/">Magento Forums</a> or <a href="http://magento.stackexchange.com/">Stack Exchange</a>.</li>
<li>Find a local <a href="https://www.meetup.com/pro/magento/">Magento Meetup</a> (or start one) to meet others who use and develop on Magento.</li>
<li>Listen to <a href="https://magento.com/blog/events/magento-masters-contributing-magento">advice from Magento Masters</a> on how to contribute (from their MageTitans panel).</li>
<li>Check out our <a href="https://devdocs.magento.com/guides/v2.1/contributor-guide/contributing.html">Contributor Guide</a> to learn how to get started contributing to DevDocs and our core code on Github.</li>
<li>If none of those work for you, check out my post on <a href="https://magento.com/blog/technical/how-get-involved-magento-community">How to Get Involved in the Magento Community</a> for even more ideas (or ask me!).</li>
</ul>
<p>Huge thank you to <a href="https://twitter.com/Fabian_ikono">Fabian Blechschmidt</a> and <a href="https://twitter.com/tobi_pb">Tobias Vogt</a> for organizing this Magento Advent Calendar, and inviting me to be a part of it, as well as to <a href="https://twitter.com/hirokazu_nishi">Hirokazu Nishi</a> for organizing the <a href="http://www.adventar.org/calendars/1372">Japanese Magento Advent Calendar</a>.</p>
<p>You, the Magento Community, are my heroes and I look forward to what else will happen as we continue to work together. Bring it on, 2017!</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 15: How to calculate a project</title>
      <link>https://www.webguys.de/diverses/how-to-calculate-a-project</link>
      <guid>diverses/how-to-calculate-a-project</guid>
      <pubDate>Thu, 15 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<h2>Introduction</h2>
<p>According to its dictionary explanation, an estimate is <strong>a guess</strong> of what the size, weight, value, amount, cost, duration of something might be.</p>
<p>It goes without saying that a <strong>prediction</strong> of what will happen <strong>in the future</strong> is something that can't be accurate and fixed by definition.</p>
<p>Nevertheless we are often asked to provide the &quot;right estimate&quot; for a software project in a short period of time (because bids don't wait) and, to make things worse, without a clear understanding of the context. </p>
<h2>Why estimates don't work</h2>
<p>Before presenting my approach to calculating a project cost, I'd like to recap the two key factors that undermine estimates accuracy:</p>
<ul>
<li>Accidental complication</li>
<li>Unknown unknowns</li>
</ul>
<h3>Accidental complication</h3>
<p>In his famous video <a href="https://vimeo.com/79106557">&quot;7 Minutes, 26 Seconds, and the Fundamental Theorem of Agile Software Development&quot;</a>, J. B. Rainsberger shows how the total cost of a feature can be expressed as the sum of the cost of <strong>essential complication</strong> with the cost of <strong>accidental complication</strong>. </p>
<p>Essential complication is due to the fact that <strong>the problem we have to solve is hard</strong>. But, as domain experts, we can exploit our skills, discover similarities and be good enough at estimating the cost of essential complication.</p>
<p>Accidental complication is due to the fact that <strong>we are not so good at our jobs</strong>. It's the errors we make at designing systems; it's the mess we leave in our code because we have no time to clean it up; it's the overhead we introduce when we add people to a project (thinking that <em>nine women can make a baby in one month</em>). </p>
<p>As we can guess, the list is a long one and the following comic strip by <a href="http://heeris.id.au/2013/this-is-why-you-shouldnt-interrupt-a-programmer/">Jason Heeris</a> is just another hilarious but meaningful example of accidental complication:</p>
<figure><img src="https://www.webguys.de/content/5-diverses/20161215-how-to-calculate-a-project/ProgrammerInterrupted.png" alt="ProgrammerInterrupted"></figure>
<p>Since the cost of <strong>accidental complication dominates</strong> the one of essential complication, one way to make the total cost more predictable is reducing accidental complication as much as we can.</p>
<h3>Unknown unknowns</h3>
<p>Another aspect that has a very big impact on estimates accuracy is our attitude to <strong>ignore events</strong> that are outside the model under investigation.</p>
<p>Indeed it's easier to focus only on the <strong>known unknowns</strong>, that are things that <em>we know we don't know</em>. </p>
<p>For example, if a requirement is &quot;I want marketing automation&quot; we don't know what exact features the customer needs but, since we know what's unknown, we can make some analysis on that domain to provide a better estimate of the cost.</p>
<p>Here is another, not so highly improbable, example of <strong>unknown unknown</strong>: a platform security patch released in the middle of the project development which breaks, say, our brand new integration with the marketing automation system. Sounds familiar, doesn't it?</p>
<p>We can't predict such an event because it isn't under our control. What's more, we can't avoid applying the patch, since it solves a security issue that has just become public and exposes the customer to potential risks.</p>
<p>Sure enough, the patch release has <strong>nothing to do with the domain</strong> we were focused on and every minute spent dealing with it isn't included in our estimate.</p>
<p>We can fill out a detailed list of highly improbable events that can occur, the so called <strong>black swans</strong> that change the game and send our estimates up in smoke. </p>
<p>But we can't rely on such list because, like it or not, uncertainty will always find it's way into the project in new, fancy and unpredictable ways.</p>
<h2>Calculating a project cost</h2>
<p>As we have seen so far, to calculate a project cost we can't take into account only essential complication but also accidental complication and uncertainty.</p>
<p>Thus, to estimate the cost of a project development, we can apply the following rules:</p>
<ul>
<li>Reach consensus on essential complication</li>
<li>Do our best to reduce accidental complication</li>
<li>Prepare for change</li>
</ul>
<h3>Reach consensus on essential complication</h3>
<p>We know how to estimate essential complication: we can count on our attitude to <strong>find similarities</strong> and weight features based on our past experience.</p>
<p>Write down all the features; we can choose the formalism we prefer but let's consider writing them down as <a href="https://en.wikipedia.org/wiki/User_story">user stories</a>. The main benefit of user stories, to me, is their focus on <strong>purpose</strong>, that is the reason why a feature is needed.</p>
<p>Then gather <strong>the team</strong> that will be responsible of bringing those requirements to life. I use the general term <em>team</em> and not <em>developers</em> because a feature is not necessarily a matter of pure software development. Ideally even the customer should take part to this activity, mainly to provide more details during discussion.</p>
<p>Ask the team to estimate the <strong>effort</strong> of development of each feature.</p>
<p>To express the effort we can use man-hours, man-days, story points, bananas or another unit we like.</p>
<p>Whatever the unit, I warmly suggest to <strong>use a non-linear sequence</strong> like: 1, 2, 3, 5, 8, 13, 20, 40, 100.</p>
<p>This way the <strong>higher the estimate</strong> the more the people will be obliged to make a choice between distant numbers, unleashing <strong>uncertainty</strong> and generating <strong>more discussion and details</strong>.<br />
This, in turn, will likely bring to break the requirement into <strong>smaller</strong> and more estimable chunks.</p>
<p>For each requirement:</p>
<ul>
<li>each team member writes down his or her <strong>own estimate</strong> without sharing it with others until the next step;</li>
<li>all members' estimates are revealed and compared one by one; differences generate  <strong>discussion</strong> that goes on until the team reaches the <strong>consensus</strong> on a number;</li>
<li>if consensus isn't reached in a reasonable amount of time (minutes) it likely means that some information is missing; ask <strong>more details</strong> to the customer, if possible, and estimate again; alternatively trust the estimate from the ones who have some past <strong>experience</strong> on the development of a similar requirement.</li>
</ul>
<p>This technique is called <a href="https://en.wikipedia.org/wiki/Planning_poker">planning poker</a> and it definitely makes the estimating process <strong>more fun</strong>. And if estimating is fun, people will be willing to do it.</p>
<p>It takes a while to get used to this technique but after the initial inertia it's impressive how fast it turns out.</p>
<p>I've used this technique a lot (both using printed cards or <a href="http://www.planitpoker.com/">this free tool</a>) and the real benefit it gives is the <strong>discussion</strong> and the seek for <strong>details</strong> that it generates.</p>
<h3>Do our best to reduce accidental complication</h3>
<p>Dealing with the <strong>root causes</strong> that produce accidental complication is a good way to reduce it. </p>
<p>For example, performing code <strong>refactoring</strong> is a good way to &quot;clean the kitchen&quot; and wipe out <strong>technical debt</strong>, that is, accidental complication. </p>
<p>Writing <strong>automated tests</strong> is a proven way to encourage refactoring. </p>
<p><strong>Test driven development</strong> (TDD) lets us design better systems, because it avoids premature optimizations and over-engineering.</p>
<p><strong>Pair programming</strong> and <strong>code review</strong> are other techniques that can help us catching design defects earlier.</p>
<p>Useless to say, learning and using <strong>solid programming principles</strong> and the best available <strong>tools and habits</strong> is gold.</p>
<p>Since there aren't <em>work-for-all</em> recipes, my advice is that of trying them and choosing those that give us concrete benefits.</p>
<p>To accomplish this goal it's important to <strong>measure</strong> the outcome: write down the time we spend developing features, use different techniques to develop the same (or equal-weighted ones) and compare the result. Measuring is the key.</p>
<p>Note: before the planning poker session begins we should be clear with the team: the <strong>additional cost</strong> of applying practices that reduce accidental complication should be taken into consideration.</p>
<h3>Prepare for change</h3>
<p>If we accept the idea that, sooner or later, <strong>uncertainty will mess up things</strong> within the project, we need to be <strong>prepared for change</strong>.</p>
<p>Since unknown unknowns can impact both project costs and delivery dates, embracing change means giving up the illusion that we can control every aspect of the project through a long-term plan (usually represented with a Gantt chart).</p>
<p>Just like a sat nav system does when it drives us from A to B, we need to be ready to recalculate our route as soon as an unexpected event occurs and changes the plan.</p>
<p>This likely means that project costs increase or delivery dates change, so our customer should be prepared. Obviously nobody is happy if this happens but after all we are not paid solely to work for a fixed amount of time or money, we are hired to reach a goal. If something obstacles us, the best we can do is finding alternatives to reach that goal; stopping in the middle of a project not to exceed a fixed time or budget doesn't bring any value to the customer.</p>
<p>Let's consider that exceeding time or budget is not always mandatory: we can reduce the scope and make our best to respect time and budget constraints. </p>
<p>To be able to reduce the scope it's essential to <strong>prioritize</strong> tasks and define what can be postponed or dropped out when we will encounter black swans.</p>
<h2>Conclusion</h2>
<p>As we have seen, calculating a project cost is not only a matter of estimating <strong>how difficult</strong> it is to develop a set of features.</p>
<p>There are <strong>other factors</strong> that have a huge impact and that have to be taken into consideration in order to give a prediction that is as close as possible to reality.</p>
<p>Nevertheless, we can't avoid to be prepared to <strong>embrace change</strong>, because thinking that everything can be under control is an illusion.</p>
<p>If you have your opinion about some of the tools and techniques I mentioned in this article or have different experiences you like to share, <strong>please leave a comment</strong> or <a href="https://twitter.com/aleron75">contact me</a> and let's discuss about that.</p>
<h2>Bibliography</h2>
<ul>
<li>Duarte, Vasco. No Estimates: How to measure project progress without estimating. OikosofySeries, 2016. Web. <a href="http://noestimatesbook.com/"><a href="http://noestimatesbook.com/">http://noestimatesbook.com/</a></a>.</li>
<li>Rainsberger, J. B. 7 Minutes, 26 Seconds, and the Fundamental Theorem of Agile Software Development. Web. <a href="https://vimeo.com/79106557"><a href="https://vimeo.com/79106557">https://vimeo.com/79106557</a></a></li>
<li>Brooks, Frederick P. The Mythical Man-month: Essays on Software Engineering. Reading, MA: Addison-Wesley Pub., 1982. Print.</li>
<li>Fowler, Martin, and Kent Beck. Refactoring: Improving the Design of Existing Code. Boston: Addison-Wesley, 2000. Print.</li>
<li>Taleb, Nassim Nicholas. The Black Swan: The Impact of the Highly Improbable. New York: Random House Trade Paperbacks, 2010. Print.</li>
<li>Taleb, Nassim Nicholas. Antifragile: Things That Gain from Disorder. New York: Random House Trade Paperbacks, 2014. Print.</li>
</ul>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 14: Understanding the blank theme of Magento 2</title>
      <link>https://www.webguys.de/magento-2/understanding-the-blank-theme-of-magento-2</link>
      <guid>magento-2/understanding-the-blank-theme-of-magento-2</guid>
      <pubDate>Wed, 14 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<h2>Prerequisites</h2>
<h3>LESS concepts to know</h3>
<h4>The definition of a variable can be placed <strong>after</strong> its assignment:</h4>
<pre><code class="language-less">#selector { attribute: @foo; }
@foo: bar;

// Result:
#selector { attribute: bar };
</code></pre>
<h4>The last assignment of a variable wins:</h4>
<pre><code class="language-less">#selector { attribute: @foo; }
@foo: foo;
@foo: bar; // wins</code></pre>
<h4><code>@import "file"</code> defaults to <code>@import (once) "file"</code>:</h4>
<pre><code class="language-less">@import 'foo.less';
@import 'bar.less';
@import 'foo.less'; // ignored
@import (multiple) 'foo.less'; // imported a 2nd time</code></pre>
<h3>The Magento 2 approach to mobile first</h3>
<p>In its default themes Magento splits CSS into two main CSS files:</p>
<ol>
<li>styles-m.css (common and mobile related definitions)</li>
<li>styles-l.css (desktop related definitions)</li>
</ol>
<p>The desktop styles are loaded &quot;on top&quot; of the mobile ones and will only be effective above a certain breakpoint (defined in Layout XML and rendered in the HTML <code>&lt;head/&gt;</code> section.</p>
<h3>Quick reminder: Fallback &amp; Code generation in Magento 2</h3>
<p>If a requested CSS file is not in it's place, Magento will look for a .less file with the same name and then trigger server side compilation (assuming developer mode is enabled).</p>
<p>We always want to compile our LESS to CSS via the provided grunt tasks, as it is much more perfomant.</p>
<p>While compiling, the Magento fallback system will be respected for requested LESS resources. See <a href="http://devdocs.magento.com/guides/v2.1/frontend-dev-guide/themes/theme-inherit.html">Magento 2 Developer Documentation</a> for details.</p>
<h2>Defining the &quot;entry points&quot; in Layout XML</h2>
<p>In <code>magento/vendor/magento/theme-frontend-blank/Magento_Theme/layout/default_head_blocks.xml</code> the before mentioned CSS entry points are defined. Notice the media query for <code>styles-l.css</code>:</p>
<pre><code class="language-xml">&lt;?xml version="1.0"?&gt;
&lt;page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"&gt;
    &lt;head&gt;
        &lt;css src="css/styles-m.css"/&gt;
        &lt;css src="css/styles-l.css" media="screen and (min-width: 768px)"/&gt;
        &lt;css src="css/print.css" media="print"/&gt;
    &lt;/head&gt;
&lt;/page&gt;</code></pre>
<p>This will be rendered to the following HTML in the pages <code>&lt;head/&gt;</code> section:</p>
<pre><code class="language-html">&lt;link  rel="stylesheet" type="text/css"  media="all" href="http://your-site.com/static/frontend/&lt;Vendor&gt;/&lt;theme&gt;/&lt;locale&gt;/css/styles-m.css" /&gt;
&lt;link  rel="stylesheet" type="text/css"  media="screen and (min-width: 768px)" href="http://your-site.com/static/frontend/&lt;Vendor&gt;/&lt;theme&gt;/&lt;locale&gt;/css/styles-l.css" /&gt;</code></pre>
<h2>Scaffolding common and mobile styles</h2>
<p>In <code>magento/vendor/magento/theme-frontend-blank/web/css/styles-m.less</code> a lot of things happen, so we will analyze it step by step. Its structure may be a little confusing, as the sequence of imports and variable assignments is not absolutely intuitive. One has to follow each <code>@import</code> statement and draw a map to get an overview - and keep in mind, that, like stated above, in LESS the assignment/use of a variable can happen <strong>before</strong> the actual definition. <a href="https://gist.github.com/fragdochkarl/9b07af0bf65aa51cb560d2a4930484f3">You can find a complete, merged, gist of the file here.</a>)</p>
<pre><code class="language-less">@import 'source/_reset.less'; // Imports: magento/vendor/magento/theme-frontend-blank/web/css/source/_reset.less

    // Resolves to:
    &amp; when (@media-common = true) {  // @media-common defined in: magento/lib/web/css/source/lib/_responsive.less
        .lib-magento-reset(); // defined in: magento/lib/web/css/source/lib/_resets.less
    }</code></pre>
<p>The above executes the <code>.lib-magento-reset()</code> Mixin, which basically returns a buch of plain CSS to render at the very beginning. <strong>Notice:</strong> Both the variable and the mixin are defined in the base lib and are not imported yet.</p>
<pre><code class="language-less">@import '_styles.less'; // Imports magento/vendor/magento/theme-frontend-blank/web/css/_styles.less

    // Resolves to:
    @import 'source/lib/_lib.less'; // Imports: magento/lib/web/css/source/lib/_lib.less
        @import '_actions-toolbar.less';
        //...
    @import 'source/_sources.less'; // Imports: magento/vendor/magento/theme-frontend-blank/web/css/source/_sources.less
        @import '_variables.less';
        @import (reference) '_extends.less';
        //...
    @import 'source/_components.less'; // Imports: magento/vendor/magento/theme-frontend-blank/web/css/source/_components.less
        @import 'components/_modals.less'; // Imports from lib: magento/lib/web/css/source/components/_modals.less
        @import 'components/_modals_extend.less'; // Imports from local: magento/vendor/magento/theme-frontend-blank/web/css/source/components/_modals_extend.less</code></pre>
<p>(For readability the code listing is shortened. <a href="https://gist.github.com/fragdochkarl/9b07af0bf65aa51cb560d2a4930484f3">You can find a complete, merged, gist of the file here.</a>)</p>
<p>Here it may become even a bit more confusing. First of all the actual base lib is included as fallback.<br />
Then most of them are overridden by local files from the blank theme. In addition <code>_extends.less</code>is imported as reference, which seems to be some redundant - considering the following import:</p>
<pre><code class="language-less">// styles-m.less; line 18
@import (reference) 'source/_extends.less';</code></pre>
<p>According to LESS behaviour, the second <code>@import</code>statement would be ignored.</p>
<p>After that, some Magento 2 Magic happens:</p>
<pre><code class="language-less">//  Magento Import instructions
//  ---------------------------------------------
//@magento_import 'source/_module.less'; // Theme modules
//@magento_import 'source/_widgets.less'; // Theme widge</code></pre>
<p>Magento 2 extends vanilla LESS by its own fallback logic. The <code>@magento_import</code> directive has to be commented out with a double slash to not interfere with the default LESS <code>@import</code> one. It iterates over all active modules and resolves every path for the given file relative to the current working directory. So the above would render to:</p>
<pre><code class="language-less">@import '../Magento_Catalog/css/source/_module.less';
@import '../Magento_Cms/css/source/_module.less';
//...
@import '../Magento_Catalog/css/source/_widgets.less';
@import '../Magento_Cms/css/source/_widgets.less';
//...</code></pre>
<p>If you would place an equivalent file to your own module it would be rendered here as well. This is an intended hook for module designers.</p>
<p>The next statement...</p>
<pre><code class="language-less">//  styles-m.less
// ...
//  Media queries collector
//  ---------------------------------------------
@import 'source/lib/_responsive.less';</code></pre>
<p>...invokes general media queries depending on the <code>@media-target</code> scope:</p>
<pre><code class="language-less">// magento/lib/web/css/source/lib/_responsive.less
// ...
&amp; when (@media-target = 'mobile'), (@media-target = 'all') { ... }
// ...
&amp; when (@media-target = 'desktop'), (@media-target = 'all') { ... }</code></pre>
<p>In our case only the first block gets executed, as of the following definition:</p>
<pre><code class="language-less">// styles-m.less
@media-target: 'mobile'; // Sets target device for this file</code></pre>
<p>Besides this, crazy magic things are happening in <code>magento/lib/web/css/source/lib/_responsive.less</code></p>
<p>According to the documentation:</p>
<blockquote>
<p>Magento UI library provides a strong approach for working with media queries. It's based on recursive call of .media-width() mixin defined anywhere in project but invoked in one place in lib/web/css/source/lib/_responsive.less. That's why in the resulting styles.css we have every media query only once with all the rules there, not a multiple calls for the same query.</p>
</blockquote>
<p>Sounds good to me, but by the actual writing of this essay, I honestly am not yet understanding how it actually works.</p>
<p>So if some of you already figured this one out, just leave a comment here or open an issue/pull request <a href="https://github.com/fragdochkarl/mage2docs/blob/master/understanding_blank_theme.md">at my github</a>.</p>
<p>Next there is a general hook for overriding the themes variables:</p>
<pre><code class="language-less">//  Global variables override
@import 'source/_theme.less';

//  Theme file should contain declarations (overrides) ONLY OF EXISTING variables
//  Otherwise this theme won't be available for parent nesting
//  All new variables should be placed in local theme lib or local theme files</code></pre>
<p>Last but not least we get another final opportunity to extend on a module basis by using a <code>_extend.less</code> file:</p>
<pre><code class="language-less">//  Extend for minor customisation
//@magento_import 'source/_extend.less';</code></pre>
<p>And thats it: <code>styles-l.less</code> works pretty much the same - just ommiting inclusion of base lib and setting desktop scope.</p>
<p>Obviously, this can only be the starting point to get productive, but I hope it provides an initial overview of the components pulled into rendering by default in Magento 2 blank theme, thus helping you kickstart your first frontend.</p>
<h2>But what would be best practice to build a new theme then?</h2>
<p>I don't think there is a distinct answer. It totally depends on the project and your personall approach.<br />
If you decide to use the built in framework - wich I neither recommend nor discourage you to do - then Magento has a couple of key concepts to consider:</p>
<ul>
<li>If you inherit from the blank theme, the Magento 2 CSS library is imported and can be used instantaneously.</li>
<li>For minor adjustments first try find a corresponding variable in <code>magento/lib/web/css/source/lib/variables</code> and overide them in your themes <code>web/css/source/_variables.less</code> file.</li>
<li>Every base lib file can be extended by putting an equivalently named file in your <code>web/css/source/</code> directory</li>
<li>Global variables are meant to be overidden in your themes <code>web/css/source/_theme.less</code></li>
<li>In addition you can use the final hook of placing a <code>web/css/source/_extend.less</code> file to your theme. Keep in mind though: this is a @magento_import directive iterating over all modules. So you could use this in your cutom module, too.</li>
<li>You can extend/overide module related styles by placing an own equivalent <code>Vendor_Module/web/css/source/_module.less</code> or <code>Vendor_Module/web/css/source/_widget.less</code> file in your theme.</li>
</ul>
<p>Further more you could even think of adapting the CSS entry points to your own need, to pull in just the base lib components you really need, for example. I mean, if you wanted to... ;)</p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 13: How to mess up talking on stage.</title>
      <link>https://www.webguys.de/diverses/how-to-mess-up-talking-on-stage</link>
      <guid>diverses/how-to-mess-up-talking-on-stage</guid>
      <pubDate>Tue, 13 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<h1>How to mess up giving a talk</h1>
<p><em>Oder auch: Was passiert eigentlich, wenn’s schief läuft.</em></p>
<p>Ihr sucht einen Artikel, in dem erzählt wird, wie ihr am besten einen Talk gebt, dann habe ich hier leider nichts für euch. In diesem Artikel geht es wirklich nur darum, wie ihr das ganze so richtig gut vermasseln könnt.</p>
<p><strong>Aber warum?</strong><br />
Es gibt eine enorme Menge an sehr guten Artikeln und Videos darüber, wie es am Besten gelingt, einen Talk auf einer Konferenz oder einer Usergroup zu geben. Ich möchte mich nicht in diese Reihe eingliedern, sondern vielmehr aufzeigen was passiert, wenn es schief läuft.</p>
<p><strong>Aber warum?</strong><br />
Ganz einfach, ich würde gern sehen, dass sich mehr Leute auf die “Bühne” trauen und sprechen.</p>
<p>Einige Links zu oben genannten, sehr guten Artikeln und Videos, findet ihr übrigens am Ende des Artikels, solltet ihr auf der Suche nach eben diesen sein.</p>
<p>Na dann los, ich teile den Prozess des auf der Bühne Sprechens in <strong>drei Teile</strong> auf, in denen so einiges, in verschiedener Form, schief gehen kann.</p>
<blockquote>
<p>Auf der Bühne stehen und sprechen besteht nämlich in der Tat nicht nur daraus eben dieses zu tun, sondern auch aus einem Davor und einem Danach.</p>
</blockquote>
<h2>Teil 1 - Davor:</h2>
<p><em>Was kann ich machen, um vorher schon sicher zu stellen, dass mein Talk in die Hose geht?</em></p>
<p><strong>Die richtige Idee für den Talk</strong> ist nicht leicht zu finden, korrekt?<br />
Eigentlich ist es total einfach. Nehmt das Nächstbeste, über das ihr gerade Schimpft oder etwas, das ihr schon ewig kennt. Achtet dabei allerdings nicht darauf, ob es jemanden interessiert.</p>
<p><strong>Ein paar Beispiele:</strong></p>
<ul>
<li>Installation und Deployment von Magento 2 ;)</li>
<li>Geschwindigkeit von und Programmierung in Typo3.</li>
<li>Die Benutzung von Strg+C, Strg+V.</li>
<li>Hello World in JavaScript.</li>
<li>Eine ToDo Liste, in welcher Sprache auch immer.</li>
</ul>
<p>Es gibt hier sicherlich noch viel mehr Themen, allerdings haben wir mit dem Thema alleine noch nichts in den Sand gesetzt. Selbst eine Stunde über STRG+C und V kann durchaus, bei hohem Entertainment-Wert, ein genialer Talk werden. Wir müssen uns also noch etwas mehr anstrengen.<br />
Wichtig ist, dass ihr das Thema entweder gar nicht kennt oder sowas von auswendig, das ihr keine Ahnung mehr habt wie die einfachsten Dinge funktionieren. Zum Beispiel wäre es geradezu genial, über die neuesten HTML Features zu sprechen, wie das alles ganz smooth über ein super tolles Framework läuft aber dabei keinen blassen Schimmer mehr zu haben, wie eigentlich ein HTML5 Grundgerüst ausschaut.</p>
<p><strong>Das passende Tool</strong> ist nicht etwa Powerpoint, wie jetzt viele meinen würden. Wir brauchen hier was richtig gutes. Wie wäre es also, wenn wir was selbst programmieren würden, das funktioniert dann auch ganz bestimmt tadellos. Aber auch ein, gerade gestern erst auf dem Markt erschienenes, neue Framework, welches ihr auf eurem Rechner unter XAMPP oder ähnlichem selber hosten müsst, erfüllt seinen Zweck.</p>
<p>Auf jeden fall nicht sowas wie Powerpoint oder gar Google Slides, wir wollen ja schließlich, dass es schief geht oder die Slides gar nicht zur Verfügung stehen.</p>
<p>Ganz wichtig in diesem Zusammenhang ist auch, dass ihr <strong>keinerlei Backup</strong> vorbereitet, also weder ein USB-Stick, mit dem ihr wegen des super fancy slide Framework ohnehin nichts anfangen könnt, noch Ideen, über was wäre wenn. Denkt einfach gar nicht darüber nach, es macht einen nur zusätzlich nervös und es wird mit Sicherheit nichts schief gehen.</p>
<p>Bei dem <strong>Erstellen der Slides</strong> achtet bitte darauf, dass ihr alle und ich meine alle tollen Funktionen des Frameworks ausnutzt, warum erkläre ich dann später im nächsten Teil. Weiterhin achtet darauf, dass alle Slides sehr gut gestaltet sind, dies ist immens wichtig. Zeitloses Design kommt übrigens immer gut an, also gerne mal verschiedene hellgraue Farbtöne auf weiß setzen, damit die Schrift schön und grazil wirkt. Auch ratsam ist, eine tolle Schriftart mit zum Beispiel ganz feinen Buchstaben oder extrem Fetten, vielleicht sogar im Wechsel, zu benutzen. Eine Webfont bietet sich an, welche dann zufällig nicht verfügbar ist, weil es auf der Konferenz nur mittelmäßiges W-LAN gibt. Hierbei ist darauf zu achten, dass die Fallback Schrift nicht auf die Slides passt aber wir machen uns ja ohnehin keine Gedanken über einen Fallback.</p>
<p>Natürlich könnten wir uns jetzt Teil 2 und 3 sparen, wenn wir einfach kurz vor der Konferenz absagen aber dies wäre etwas zu langweilig, außerdem sind wir ja der Meinung extrem gut Vorbereitet zu sein – zumindest sollten wir uns dies oft genug einreden.</p>
<p>Okay, damit sind schon einige Vorbereitungen getroffen, <strong>los geht’s auf die Bühne</strong>.</p>
<h2>Teil 2 Die Bühne:</h2>
<p><em>Wie stelle ich es am besten an, ein möglichst schlechtes Bild abzugeben?</em></p>
<p>Vorab: die <strong>Nervosität ist unser Freund</strong> und natürlich haben wir direkt vor der Präsentation noch 5 Tassen Kaffee und eine Flasche Wasser getrunken.<br />
Wir kommen natürlich <strong>zu spät zum Talk</strong>, weil wir zuvor noch schnell auf Toilette waren oder sonst wo, Hauptsache wir kommen zu spät. Denn wir wissen, wer zu spät kommt, hat automatisch einen großen Auftritt. Die Anzahl der Minuten ist hier entscheidend, zu kurz wird verziehen und oft nicht weiter bewertet, zu lang und die Leute sind unruhig, stehen schon auf und gehen. Der beste Zeitpunkt ist also, wenn die Leute anfangen unruhig zu werden, wenn nun der Talk angefangen wird und ihr euch am besten damit entschuldigt noch was wichtiges gemacht zu haben, wissen die Leute woran sie sind.</p>
<p>Nachdem die Realität einen nun vollends erschlägt und uns zeigt, wie schlecht wir vorbereitet sind, beginnt den Talk bitte unbedingt damit zu <strong>erzählen das ihr nicht vorbereitet seid</strong> und dies doch zu entschuldigen sei. Zack, schon hat das Publikum raus wie viel es euch bedeutet, nämlich gar nichts. Allerdings, gibt es immer ein paar Leute die es erst später kapieren, wir müssen uns also doch noch etwas anstrengen.</p>
<p><strong>Das Publikum</strong> verärgert ihr am besten, wenn ihr es erst gar nicht beachtet, schenkt denen einfach gar keine Aufmerksamkeit. Diese Taktik ist auch recht effektiv für den Abbau der Nervosität. Vielleicht sucht ihr euch einfach einen Punkt auf der Bühne, den ihr die ganze Zeit fokussiert. Dies kann auch die Leinwand sein, der Vorteil bei der Leinwand liegt auf der Hand, das Publikum sieht nur den Hinterkopf und ihr könnt die wildesten Grimassen schneiden, wenn jemand eine, natürlich dumme, Frage stellt. <strong>Fragen werden übrigens grundsätzlich ignoriert</strong> oder aber übergangen, je nachdem wie sicher ihr euch bei der ein oder anderen Technik fühlt.</p>
<p>Weil uns nun bewusst ist, dass wir nicht richtig vorbereitet sind, haben wir in der Nervosität natürlich vergessen, was auf den Slides steht oder über was wir eigentlich sprechen wollten. Kein Problem, wir sind ja ohnehin schon zur Leinwand zugewandt, also lesen wir einfach den Text von den Slides vor. Einige ablesefehler sind immerhin besser als gar nichts zu sagen.</p>
<p>Nun sollten auch die Hartnäckigen begriffen haben, dass ihr euch nicht um sie schert, Gratulation.</p>
<p>Um dies noch etwas abzurunden, achtet doch darauf <strong>Tatsachen</strong> mit Worten wie &gt;wer das nicht weiß, kann eh nichts&lt; oder &gt;das ist ja eine Grundvoraussetzung&lt; zu erklären, hier könnt ihr wertvolle Zeit sparen, anstatt das Publikum vom wissen her abzuholen. Einfach gar nichts zu komplexen Gegebenheiten zu sagen ist ebenfalls sehr effektiv aber eher die softe Variante, welche nervige Fragen hervorrufen könnte.</p>
<p>Natürlich haben wir auch noch unsere <strong>Slides und die ganze Technik</strong> auf der Bühne, hier können wir auch noch einiges draus machen. ihr habt hoffentlich nicht euren Laptop geladen oder etwa doch? - Wir wollten uns doch nicht vorbereiten, Pech, nun kann nur gehofft werden ihr habt genug Slides.</p>
<p>Was wir in jedem Fall haben, ist das super tolle <strong>Slide-Framework</strong>, von dem das Publikum garantiert begeistert sein wird. Durch die Nervosität und dadurch, das wir mit dem Framework so gut wie nicht vertraut sind, springen wir also teils wahllos zwischen und auf den Slides hin und her. Dies machen wir so lange bis wir es geschafft haben uns selbst total zu verwirren.</p>
<p>Natürlich haben wir auch <strong>Programme wie Pushbullet oder Browser-Notifications</strong>, genau wie die Windows Notifications nicht abgeschaltet und den Bildschirm einfach auf den Projektor dupliziert. Das ist besonders hilfreich um den Talk mit <strong>absolut unberechenbaren Informationen</strong> zu bereichern wie &gt;Schatz, hast du eigentlich deine Unterhosen gewaschen?&lt; oder &gt;Hey, hammer Party gestern, warst mal wieder total betrunken.&lt; aber auch Firmeninterna, aus dem noch geöffneten Slack-Channel, können hier die Stimmung steigern. Wir handeln schnell, wechseln von der Präsentation auf den Desktop, wo neben PornHub und wichtiger Firmendokumente der Slack-Channel darauf wartet geschlossen zu werden.</p>
<p>Kurz rot werden, schnell alles schließen und mit dem Ablesen weiterer Slides, das nun wieder aufmerksame Publikum, loswerden. Jetzt ist der Moment gekommen an dem wir, in unserem hellblauen Hemd, T-Shirt oder was auch immer, absolut sichtbar durchgeschwitzt wie ein <strong>Hochleistungssportler</strong>, mit einem hochroten Kopf da stehen und der Talk zu Ende ist.</p>
<p><strong>Wir haben es geschafft</strong>, bravo.</p>
<p>Aber Moment, warum sitzen da denn im Publikum immer noch Menschen und warum Applaudieren denn manche davon? Haben wir am Ende doch noch irgendwas ausgelassen um alles zu einem Desaster werden zu lassen?</p>
<h2>Teil 3 Danach:</h2>
<p><em>Lieber schnell weg hier.</em></p>
<p>Total geschockt davon, dass es applaudierende Leute gibt, sehen wir natürlich nun zu, dass wir so schnell wie möglich aus den durchgeschwitzten Klamotten raus kommen und <strong>verlassen sofort die Konferenz</strong>. Ansonsten würden wir ja nur noch mit unseren Erfolgen konfrontiert werden, dies gilt es zu vermeiden.</p>
<p><strong>Im Hotel oder Daheim</strong> angekommen, denken wir schon ganz anders über den Talk nach. Yeah, wir haben es geschafft und die Leute haben applaudiert, wir fühlen uns toll, schlau, geradezu überragend. Schnell Twitter, auch gerne Facebook öffnen und der Welt erzählen, was für einen grandiosen Talk wir gerade gegeben haben. Wichtig ist hier sich keine Tweets von anderen über den Talk durchzulesen, Selbstreflexion brauchen wir nicht, denn wir wissen ja, dass wir gerockt haben.</p>
<p><strong>Wir haben gerockt</strong>, Yeah, also am Besten gleich den nächsten CFP ausfüllen.</p>
<p>Sicherlich habt ihr aufgemerkt, als die Menschen am Ende wegen unserem Talk applaudiert haben, warum ist das so? Vorab, ich hab schon vieles der genannten Sachen gesehen, durch andere erzählt bekommen oder habe sie selbst vermasselt, ein zwei Sachen sind natürlich auch erfunden. Alle haben aber eines gemeinsam, es wurde der <strong>Mut</strong> aufgebracht um vor anderen, auf einer Bühne zu sprechen. Selbst wenn nur abgelesen wurde, durch Nervosität oder mangelnde Vorbereitung etwas schief geht, so ist es doch wahrscheinlich, dass einige Menschen im Publikum die <strong>Idee</strong> verstehen. Es ist sogar wahrscheinlich, dass sie was davon mitnehmen oder gar eine <strong>tolle Zeit</strong> hatten, weil sie genauso empfinden oder es <strong>amüsant</strong> war.</p>
<p>Wurde etwas extrem vermasselt, gibt es natürlich Kritik und vielleicht ist es dann auch nicht das richtige, vor anderen Menschen zu sprechen. Dennoch habt ihr es getan, eure Ideen mitgeteilt und das ist enorm wichtig. Würden wir nämlich alle unsere Ideen zurückhalten, nur weil wir nicht sprechen wollen, Angst davor haben oder besorgt sind, wäre vieles tolles auf dieser Welt nicht möglich.</p>
<blockquote>
<p><strong>Meine Bitte an euch</strong>, vermasselt es so richtig auf der “Bühne” aber sprecht, teilt eure Ideen und seid vielleicht, sobald die Nervosität sich gelegt hat, offen für Kritik und Diskussionen.</p>
</blockquote>
<p>Ich hoffe sehr, euch hat meine kleine Geschichte etwas gefallen, vielleicht habt ihr ja bald das Glück und seid auf einer Konferenz, auf der mein Talk mit selbigem Titel zugelassen wurde. Wenn ja, dürft ihr natürlich gerne auf mich zukommen und mir persönlich Kritik äußern, beziehungsweise mir Feedback geben.</p>
<p>Falls dies nicht passiert, nutzt einfach die Kommentarfunktion oder Twitter <a href="https://twitter.com/ToH_82">@ToH_82</a>.<br />
Die <strong>Slides für den Talk</strong> könnt ihr übrigens hier finden: <a href="http://slides.com/tobiashartmann/how-to-mess-up-talking">Slides.com: How to mess up talking</a>.</p>
<p>Nun wie versprochen einige <strong>Quellen</strong> die zeigen, wie ein Talk von der Ideenfindung bis hin zur Feedbackrunde, aufgebaut werden kann.</p>
<ul>
<li><a href="http://writing.jan.io/2013/05/10/how-to-give-the-killer-tech-talk-a-pamphlet.html">How To Give the Killer Tech Talk — A Pamphlet</a></li>
<li><a href="http://wunder.schoenaberselten.com/2016/02/16/how-to-prepare-and-write-a-tech-conference-talk/">How to prepare and write a tech conference talk</a></li>
<li><a href="https://www.youtube.com/watch?v=_ZBKX-6Gz6A">‘Thought Leader’ gives talk that will inspire your thoughts</a></li>
<li><a href="http://speaking.io/">THOUGHTS ON PUBLIC SPEAKING BY ZACH HOLMAN</a></li>
</ul>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 12: You Can Sit With Us.</title>
      <link>https://www.webguys.de/diverses/you-can-sit-with-us</link>
      <guid>diverses/you-can-sit-with-us</guid>
      <pubDate>Mon, 12 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Most of the authors describe their experiences how they connected to the Magento world. It was always about attending a Magento event as well as to get in touch with different people and the challenge of networking. Especially <a href="https://www.google.com/url?q=https://www.integer-net.com/an-open-approachable-magento-community-are-we-as-open-and-welcoming-as-we-should-be/?utm_source%3Dtwitter%26utm_medium%3Dpost%26utm_term%3Dblog%26utm_content%3Dcommunity%26utm_campaign%3Dapproachable&amp;sa=D&amp;ust=1481394514846000&amp;usg=AFQjCNHxjSyMZbd9Ld4xWjf0tPEGz-gO2g">Sonja’s</a> and <a href="https://www.google.com/url?q=https://medium.com/@RebeccaLTroth/being-a-magento-extrovert-is-challenging-9fd99ba4a001%23.lv8srcjab&amp;sa=D&amp;ust=1481394514847000&amp;usg=AFQjCNFqqTMHqypI_uvpA6__WXBVhvmy5g">Rebecca’s</a> approaches and feelings about how to become a member of the community are still in my mind. I can still feel that anxiety about coming into a room full of developers, how I didn’t know what to say because I was a absolute greenhorn and - of course - neither a man nor a developer. Thus my first experience is quite similar like Sonja’s or Rebecca’s story. I want to point out one brave hypothesis:</p>
<h2>There is no Magento or Meet Magento community.</h2>
<p>Or, at least, there is not THE ONE AND ONLY community of Magento and Meet Magento.</p>
<p>Let’s have a look at what “community” really means:</p>
<p><a href="https://www.google.com/url?q=http://www.dictionary.com/browse/community&amp;sa=D&amp;ust=1481394514851000&amp;usg=AFQjCNELqn2eCzOKz4NtVpFBGIzEUZ6o1g">community</a> [kuh-myoo-ni-tee], noun.</p>
<p>“a social (umm, yes), religious (...somehow…), occupational (yep), or other group sharing common characteristics or interests (hmmm don’t know what this could be…) and perceived or perceiving itself as distinct (totally agree!) in some respect from the larger society within which it exists.”</p>
<p>Since I started to work for Meet Magento Association in January, I am involved in Meet Magento and this whole community topic for almost one year. I had to struggle with the same emotions and challenges all the other newbies did, but most of all I noticed one main thing: the community is no community at all, at least not in the common sense. Magento related people spread all over the world, and this is why it is consisted of cultures, languages, people, sexes, professions and characters that can’t be more different. And each culture, each local Meet Magento event and its partners, attendees and sponsors, can be described as another, minor community.</p>
<p>Originally coming from Sociology, I learned that humans can only build personalities and understand themselves as individual, if they have an opposite. We see our counterpart with all his or her characteristics, and we understand our similarities and, much more important, our differences. That’s the way it works - to find out a community’s attributes, we need to point out the differences to the rest of the world.</p>
<p>When we step down in the complexity of this whole community-topic, we’re only exploring more and more diversity than before. And each group, each community-like bunch of people, is setting its boundaries to other groups, cultures, professions and persons to create its own personality. And going deeper in all those different microcosms, the diversity of groups gets bigger and bigger.</p>
<p>But at the end - and this is what makes the community of Magento and Meet Magento so special - there is one thing that connects us all. All people are driven by the same questions, interests, problems and same passion. However, every group just keeps dissociating itself by profiling from all the other communities that aim exactly the same as they do.</p>
<p>Despite all the network tools we use, the most important aspect for people is to meet and talk to each other. Magento events are therefore relevant. In the Magento world are so many events starting from meetups, developer activities, small and huge conferences up to Imaginge. The magic here is that nearly 98% of them are organized by people outside of Magento Inc. We all organize our eco system, not the system vendor. I can feel this heart beat worldwide in my every day work. A company can never drive such a movement. Only people outside can do it. The Magento eco system is a wonderful example for the diversity of human beings. Bringing people together and exchanging knowledge are the ties that connect us all over the world - no matter what sex we are, what professions we learned or what language we speak. It brings people from all kinds together. So let’s just use “community” in its most common sense:</p>
<p><a href="https://www.google.com/url?q=http://www.dictionary.com/browse/community&amp;sa=D&amp;ust=1481394514858000&amp;usg=AFQjCNGEWwSRLK3tyD5VxAyzre2kCx1WjQ">community</a> [kuh-myoo-ni-tee], noun.</p>
<p>“joint possession, <strong>enjoyment</strong>, liability.”</p>
<p>The year end time is a good moment to remember that we all live in one world. There may be borders and conflicts, but in the Magento community so many different people are connected and stand together. We all make a difference. For 2017 enrich our community with all your knowledge, your flaws and cranky manners, your enthusiasm, passion and <strong>enjoyment</strong>, and let us sit together.</p>
<iframe src="//giphy.com/embed/l2Je5bJHNCHYwpYeQ" allowfullscreen="allowfullscreen" width="480" height="362" frameborder="0"></iframe>
<p><a href="https://giphy.com/gifs/season-16-the-simpsons-16x7-l2Je5bJHNCHYwpYeQ">via GIPHY</a></p>]]></description>
    </item>
        <item>
      <title>T&#252;rchen 11 : Schmalspurentwicklung von Magento 1 Shops (ohne geiles Deployment und so)</title>
      <link>https://www.webguys.de/magento-1/schmalspurentwicklung-mit-magento-1</link>
      <guid>magento-1/schmalspurentwicklung-mit-magento-1</guid>
      <pubDate>Sun, 11 Dec 2016 00:00:00 +0000</pubDate>
      <description><![CDATA[<p>Wir haben alle mal klein angefangen. Die wenigsten von uns erst zu einer Zeit, in der Vagrant oder Docker zum üblichen Arbeitsumfang gehört haben. Die letzten 8&nbsp;Jahre, in denen wir nun mit Magento arbeiten konnten, haben sich irre viele Tools entwickelt, die einem das Leben leichter machen können. Dummerweise muss man sich mit all dem neuen Kram erst tagelang beschäftigen, bevor man eine einzige produktive Zeile herausquetschen kann.</p>
<p>Wenn es nun damit getan wäre, könnte man sich natürlich 14 Tage hinsetzen und alles Mögliche einrichten - StackOverflow sei Dank, wird es in 50&nbsp;% der Fälle sicher dann auch laufen. Aber was ist, wenn wir etwas anpassen müssen oder ein Fehler ums Verrecken einfach nicht verschwinden will? Dann stehen wir da - meist mit runtergelassener Hose.</p>
<h2>Herausforderungen bei Magento 1</h2>
<p>Bei der Entwicklung von Magento 1 gibt es einige Herausforderungen:</p>
<ol>
<li>Wie speichere ich das ganze Projekt, damit ich es auch auf dem Kundenserver wieder ausspielen kann?</li>
<li>Wie erfasse ich meine Änderungen?</li>
<li>Wie verkürze ich zwingend notwendige, übliche Arbeitsschritte?</li>
</ol>
<p>Dummerweise gibt es diese Fragen nicht nur bei der reinen PHP Entwicklung. Bei Websites tanzen wir ja auf den verschiedensten Hochzeiten: HTML, CSS, JavaScript, PHP, SQL. Jeder dieser Bereiche hat seine eigenen Herausforderungen und zwingt uns, eigene Tools zu verwenden.</p>
<p>Viele Lonesome Rangers, die sich nicht in großen Agenturen zu Zahnrädchen haben machen lassen, können meist nicht die Zeit und das Geld aufwenden, um all diese Anforderungsbereiche mit Bravour zu meisten. Meine Meinung: Ist auch gar nicht nötig. Nutzt lieber weniger Tools, aber habt euren Workflow fest im Griff. Dann könnt ihr besser mithalten, als diejenigen, die sich in immer wieder neuen, hippen Technologien verlieren. Ja: Diese neuen Technologien helfen - ohne Frage. Aber sie helfen nur denen, die sie auch meistern können und dafür ist leider immer mehr und immer komplexeres KnowHow nötig.</p>
<p><small><em>Eine <strong>einschränkende Vorbemerkung</strong> muss ich leider noch machen: Ich arbeite seit etlichen Jahren nun ausschließlich mit OS X und kann für die Webentwicklung auch ausschließlich Unix basierte Betriebssysteme empfehlen. Es hat sich in meinen vielen Schulungen immer wieder - und mit Magento 2 noch eklatanter - gezeigt, dass Windows zwar super für den Gaming- und Office-Einsatz geeignet, aber für solide Webentwicklung einfach scheiße ist.</em></small></p>
<p><small><em><strong>Werbedisclaimer:</strong> Einige der hier genannten Tools kosten Geld. Ich verlinke zwar, verdiene aber nichts am Verkauf der Lizenzen. Ich bin mit den Programmen einfach sehr zufrieden und kann sie daher empfehlen. Unbedingt Geld ausgeben muss man aber nicht - auch wenn es zum Geld <strong>Verdienen</strong> meistens unerlässlich ist, auch welches auszugeben.</em></small></p>
<hr />
<h2>TL;DR</h2>
<ul>
<li>Nutzt auf jeden Fall Git und haltet euch an GitFlow!</li>
<li>Entwickelt lokal mit MAMP oder XAMP und vergesst Xdebug nicht!</li>
<li>Um PHPStorm führt kein Weg herum, vergesst nicht Magicento!</li>
<li>Geht mit SSH auf eure Kundenserver und nutzt dort ebenfalls Git!</li>
</ul>
<p>Und wer's genauer und noch mehr wissen will, steht jetzt auf, holt sich was weihnachtlich Leckeres zu trinken (Kaffee, Kakao, Tee …) und liest weiter! Viel Spaß … es ist viel geworden ;-)</p>
<hr />
<h2>08/15 Werkzeug-Gürtel</h2>
<p>Was braucht es also, um grundsätzlich <strong>ziemlich</strong> sauber mit Magento 1 entwickeln zu können?</p>
<ul>
<li>Eine … naja, DIE ordentliche IDE: PHPStorm (notfalls Derivate wie zum Beispiel Netbeans).
<ul>
<li>Bei PHPStorm: Magicento</li>
</ul></li>
<li>Git (mit zusätzlicher GUI: z. B. SourceTree)</li>
<li>Lokale Serverumgebung (z. B. MAMP Pro) mit 
<ul>
<li>Apache 2</li>
<li>PHP 5.6 (Magento 1 ist nur mit Patches auf PHP 7.x lauffähig)</li>
<li>MySQL 5.6 (notfalls auch 5.5)</li>
</ul></li>
<li>SSH Zugänge zum Server</li>
</ul>
<h3>PHPStorm</h3>
<figure class="alignleft img-responsive half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/icon-phpstorm.svg" alt="icon-phpstorm"></figure>
<p>PHPStorm von Jetbrains hat sich als die State-of-the-Art IDE herausgearbeitet. Mit vielen wirklich nützlichen Features für PHP-Entwickler macht es einem das tägliche Entwicklerleben wirklich einfach. Um nur zwei zu nennen, die einem bei der Magento-Entwicklung helfen: CodeNavigation (mit einem Klick zur Implementation einer Klasse springen) oder CodeCompletion (beim Schreiben direkt mögliche Methoden vorgeschlagen bekommen).</p>
<p>Großer Vorteil für uns Entwickler ist dabei die extrem gute Performance bei diesen Aufgaben. Öffnet man ein Projekt zum ersten Mal, lastet PHPStorm den Rechner inkl. aller Cores aus und indiziert das gesamte Projekt. Dieser Vorgang dauert je nach Rechner und Projektgröße gerne mal 4-5 Minuten, anschließend ist dieser Index im Cache und aktualisiert sich on the fly, sodass das Suchen nach Methoden oder Dateien anschließend verzögerungsfrei passiert. </p>
<p>Oft wird PHPStorm mit Netbeans verglichen. Vor Jahren hatte auch ich mit Netbeans gearbeitet, aber wer einmal PHPStorm gesehen und vielleicht sogar benutzt hat, will nichts anderes mehr zum Programmieren verwenden, weil es einfach irrsinnig schnell und ungemein unterstützend ist.</p>
<p>Für PHPStorm legt man normalerweise Geld auf den Tisch. Es gibt aber auch das sogenannte <a href="https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Early+Access+Program">EAP (Early Access Programm)</a> bei dem man sich die aller neueste Beta für (immer wieder) 30 Tage kostenlos installieren kann.</p>
<p>PHPStorm bietet selbst bereits Clients für mySQL und Versionsverwaltungssysteme (in unserem Fall: git) und integriert deren Informationen dann nahtlos in den Editor. Das hat zur Folge, dass geschriebenes SQL direkt mit der Datenbankstruktur verglichen wird und Fehler oder Inkonsistenzen im Code markiert werden können. Die Git Integration zeigt einem bereits im Editor an, welche Zeilen hinzugefügt, geändert oder entfernt wurden und kann auch direkt die History der geöffneten Datei aufschlüsseln.</p>
<p>Ich selbst arbeite aber mit externen Tools, schlicht weil mir die doch sehr entwicklerlastige GUI in PHPStorm nicht wirklich zusagt. Die Tools an sich sind aber mächtig und durchdacht. Wer sich also SequelPRO oder SourceTree nicht antun möchte, kommt mit PHPStorm bequem und funktionsgewaltig durchs normale Entwicklerleben.</p>
<p>Wenn ihr noch kein PHPStorm nutzt, solltet ihr euch mal <a href="https://www.jetbrains.com/phpstorm/documentation/">in den Dokumentationsvideos von Jetbrains</a> umsehen. Sogar alte Hasen können bei den Features noch das eine oder andere Kleinod entdecken.</p>
<h4>Magicento</h4>
<figure class="alignleft img-responsive half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/magicento-logo.png" alt="magicento-logo"></figure>
<p>PHPStorm hat noch einen weiteren Vorteil: Enrique Piatti ist selbst Magentician und entwickelt mit viel Hingabe <a href="http://magicento.com/">Magicento, ein PlugIn für PHPStorm</a>, das die Entwicklung von Magento&nbsp;1 Inhalten (Extensions, Themes, etc.) massiv beschleunigt und die Fehlerquote drastisch senkt.</p>
<p>Magicento kostet mittlerweile pro Jahr 39 USD, die ich persönlich aber wirklich gerne bezahle. Ich gebe zu, ich bin ein Sparfuchs, und wenn es nicht sein <strong>muss</strong>, versuche ich mich auch um solche Beträge zu drücken. Aber für Enriques PlugIn würde ich sogar 50 USD im Jahr bezahlen, alleine deswegen, weil es unheimlich viele Standardarbeiten automatisch erledigen kann und mir somit mehrere Stunden Arbeit im Monat spart:</p>
<ul>
<li>Anlegen eines neues Moduls
<ul>
<li>Wahlweise mit oder ohne: Models, Blocks, Helpers, SetupScripts</li>
<li>AdminGrids inkl. Controller</li>
</ul></li>
<li>Theme-Arbeiten
<ul>
<li>Duplizieren einer Quelldatei an die richtige Stelle</li>
<li>Vergleich von Custom vs. Parent Theme</li>
</ul></li>
<li>Auflösen von Magento Factorynames ('catalog/product') für
<ul>
<li>CodeCompletion</li>
<li>CodeNavigation</li>
</ul></li>
</ul>
<p>Und hier ist die Liste noch lange nicht zu ende.</p>
<h3>Git</h3>
<figure class="alignleft img-responsive half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/git-logo.png" alt="git-logo"></figure>
<p>An einer Versionsverwaltung (VCS) kommt man beim professionellen Arbeiten nicht mehr vorbei. Alles andere würde auch bei groben Fehlern schnell dazu führen, dass ihr komplett von vorne beginnen müsst.</p>
<p>Selbst wenn ihr also ganz alleine und nur auf eurem eigenen Rechner arbeitet, solltet ihr Git einsetzen, um eure eigene Arbeit nachvollziehbar und reproduzierbar zu speichern.</p>
<h4>Warum Git?</h4>
<p>Im Gegensatz zu anderen VCS ist <a href="https://git-scm.com">Git</a> dezentral aufgebaut. Das bedeutet, dass es eigentlich keinen zentralen Server gibt. Daher werden Dateien bei Git auch nicht &quot;ausgecheckt&quot; und sind damit für die Bearbeitung durch andere gesperrt.</p>
<p>Git spielt seine Stärke vielmehr in der Zusammenarbeit aus. Jeder Bearbeiter hat eine komplette Kopie des Projekts (das <strong>Repo</strong>sitory) mit der gesamten Projektvergangenheit (Commit History) auf dem eigenen Rechner. </p>
<p>Arbeiten nun mehrere Leute parallel an einem Projekt, arbeitet erstmal jeder für sich auf seinem eigenen System in seinem eigenen Repo. Ist die eigene Arbeit abgeschlossen, synchronisiert man sein Repo mit den Repos der anderen Kollegen. Das kann natürlich bei 3 Kollegen müßig werden. Immerhin müsste jeder Kollege dann per TCP/IP erreichbar sein.</p>
<p>Hier kommen jetzt Dienste wie Bitbucket oder GitHub ins Spiel. Diese Dienste sind quasi auch nur ein weiterer Bearbeiter, mit dem Unterschied, dass sie keine Änderungen an eurem Projekt vornehmen, sondern lediglich eine Zentrale bieten, mit der sich alle synchronisieren können.</p>
<p>Weil Git dabei aber nur Änderungen (Diffs) der Dateien synchronisiert, ist zum einen die übertragene Datenmenge verhältnismäßig gering und es kommt eher selten zu Konflikten in Dateien, die von mehreren Personen parallel bearbeitet wurden.</p>
<p>Es gibt <a href="https://try.github.io/levels/1/challenges/1">ein interaktives Tutorial</a>, in dem man sich mit Git vertraut machen kann, wenn man noch nie mit Git gearbeitet hat. In 15 Minuten habt ihr, denke ich, ein ganz gutes Verständnis dafür, was Git genau ist und wie es euch bei eurer Arbeit helfen kann.</p>
<h4>Arbeitsablauf mit Git</h4>
<p>Damit nicht jeder in Git grad so rumarbeitet, wie es ihm gefällt, hat sich <a href="http://nvie.com/posts/a-successful-git-branching-model/">Vincent Driessen</a> 2010 Gedanken gemacht, wie die Arbeit in Git sinnvoll und strukturiert ablaufen kann. Herausgekommen ist dabei: <strong>GitFlow.</strong></p>
<p>GitFlow ist im Grunde kein eigenes Programm oder eine Ergänzung zu Git selbst, sondern viel mehr eine Art Ablaufbeschreibung, wie man in Git arbeiten sollte, wenn man bestimmte Aufgaben zu erfüllen hat.</p>
<p>Natürlich gibt es <a href="https://github.com/nvie/gitflow">für die Kommandozeile auch kleine Helferlein</a>, die diese Arbeitsschritte automatisiert ausführen können; technisch zwingend nötig sind sie aber nicht.</p>
<p>Einen guten Einblick in die Vorteile des Arbeitens mit GitFlow bekommt man auf <a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow">einer Tutorialseite von Atlassian</a>, die mit SourceTree auch ein kostenloses GUI-basiertes Git-Werkzeug anbieten, das GitFlow direkt unterstützt. Mehr dazu im nächsten Abschnitt.</p>
<p>Welche Aufgaben soll GitFlow denn nun eigentlich strukturieren:</p>
<ol>
<li>Erarbeiten neuer Funktionen (Features)</li>
<li>Sicherstellen des Zustands der Staging-Umgebung (Development)</li>
<li>Sicherstellen des Zustands der Live-Umgebung (Master)</li>
<li>Integration von Notfall-Patches (Hotfixes)</li>
</ol>
<p>Dabei ist es GitFlows Ziel, dass wir als Entwickler auf unseren Lokalen Maschinen in Feature-Branches entwickeln, die zu einem bestimmten Zeitpunkt aus dem Develop-Branch abgezweigt werden. Ist unsere Arbeit abgeschlossen und hinreichend getestet, mergen wir unseren Feature-Branch wieder in den jetzt vielleicht überarbeiteten Develop-Branch und stellen unsere Arbeit damit unseren Kollegen und im Staging-System zur Verfügung.</p>
<p>Sind alle Tests abgeschlossen und hat der Kunde sein Okay gegeben, erzeugt man aus dem aktuellen Stand des Staging-Systems (also dem Develop-Branch) ein Release und merged alle bis dahin aufgelaufenen Änderungen in den Master-Branch.</p>
<p>Wichtig bei GitFlow ist: Niemand arbeitet im Master-Branch und wenn möglich arbeitet auch niemand im Develop-Branch. Diese Branches werden immer nur durch einen Merge geändert.</p>
<p>Was nun aber, wenn man einfach mal gravierend Scheiße gebaut hat oder ein anderes Problem im Livesystem zutage tritt? Nun, dann macht man einfach einen Hotfix und GitFlow sorgt dafür, dass diese Änderung automatisch im Master- und Develop-Branch landet, ohne dass man diesen zweiten Merge händisch selbst ausführen müsste.</p>
<p>Alles in allem erleichtert einem GitFlow also das strukturierte und kollaborative Arbeiten ungemein und ich empfehle jedem, diesen Workflow auch dann zu nutzen, wenn man mutterseelenallein auf der eigenen Maschine vor sich hinentwickelt.</p>
<h4>GUI für Git</h4>
<figure class="alignleft img-responsive half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/sourcetree-logo.png" alt="sourcetree-logo"></figure>
<p>Wie ich im Abschnitt PHPStorm schon erwähnt habe, ist ein eigenes Tool für die Arbeit mit Git im Grunde nicht zwingend nötig. Man kann es wahlweise wie ein Berserker wild auf der Kommandozeile treiben, oder man quält sich durch die hässliche PHPStorm-GUI-Implementation von Git; oder aber, man nutzt eben doch einfach ein weiteres, schickes Tool: <a href="http://sourcetreeapp.com">SourceTree von Atlassian</a></p>
<p>Großer Vorteil: SourceTree ist kostenfrei verfügbar. Ich gestehe auch: Es könnte von der GUI <em>etwas</em> stylischer sein. Aber im Vergleich zur etwas schickeren, dafür aber kostenpflichtigen Alternative <a href="https://www.git-tower.com/mac/">Tower</a> ist es wenigstens performant.</p>
<p>SourceTree bietet euch eine schöne Übersicht über euer aktuelles Projekt mit der gesamten History, einer Liste mit allen Branches, die bei euch auf dem Rechner liegen und den Branches inkl. der History auf den &quot;Remotes&quot; (die Kopien der Repos bei Bitbucket, Github oder euren Kollegen).</p>
<p>Mit funktionalen Buttons in der Toolbar könnt ihr schnell und zuverlässig auf die üblichen Funktionen zugreifen.</p>
<h4>Git und Magento</h4>
<p>Es gibt bei der Arbeit in einem VCS mit Magento zwei unterschiedliche Ansätze:</p>
<ol>
<li>Alles liegt im Git</li>
<li>Es liegen nur die neuen oder geänderten Dateien im Git, nicht aber die komplette Installation</li>
</ol>
<p>Wir haben im <a href="http://buro71a.de/">Büro 71a</a> beide Varianten ausprobiert. Unsere Projekte sind häufig &quot;Feuerwehr&quot;-Projekte, die wir von Agenturen übernommen haben, die sich mit Magento übernommen haben. Oftmals gibt es hier CoreHacks, sodass wir Git verwenden, um uns schnell über solche Änderungen zu informieren. </p>
<p>Dazu ist es aber unerlässlich, dass der gesamte Code des Projektes —&nbsp;inklusive Magento&nbsp;– im Repo gespeichert wird. Ändert sich also &quot;heimlich&quot; eine Core Datei, sehen wir das sofort. Diese Datei zeigt sich dann entweder als &quot;geändert&quot; oder taucht in der History. Für uns kommt daher mittlerweile nur noch Variante 1 in Frage.</p>
<p>Variante 2 hat zudem den Nachteil, dass man eine sehr umfangreiche <code>.gitignore</code> Datei pflegen muss. Git selbst meldet sonst nämlich alle Core-Dateien als &quot;neue Datei&quot; und würde diese am liebsten gerne ins Repo übernehmen. Mit <code>.gitignore</code> kann man sicherstellen, dass Git diese Dateien und Ordner gar nicht erst sieht. Je länger jedoch diese <code>.gitignore</code> Datei wird, umso langsamer wird auch git.</p>
<p>Git unterstützt übrigens zwei unterschiedliche <code>.gitignore</code> Dateien. Eine liegt im Repo und wird für gewöhnlich mit allen anderen geteilt. Eine weitere liegt und gilt global auf eurem Rechner.</p>
<h5>Globale <code>.gitignore_global</code></h5>
<p>Ich nutze die globale Datei, um die Standardordner in meinem Projekten, die nichts im Repo zu suchen haben, direkt von vornherein auszuschließen. Diese Ordner sind zum einen systembedingt zum anderen von meiner Arbeitsweise geprägt:</p>
<p><pre class="highlight"><code>*~
.DS_Store
.idea
.modman
app/code/community/Aoe/TemplateHints
app/etc/modules/Aoe_TemplateHints.xml
skin/frontend/base/default/aoe_templatehints

app/code/community/Hackathon/EmailPreview
app/design/adminhtml/default/default/layout/hackathon_emailpreview.xml
app/etc/modules/Hackathon_EmailPreview.xml
app/locale/en_US/Hackathon_EmailPreview

app/code/community/Mgt/DeveloperToolbar
app/code/local/Mgt/Base
app/design/adminhtml/default/default/layout/mgt_developertoolbar.xml
app/design/adminhtml/default/default/template/mgt_developertoolbar
app/design/frontend/base/default/layout/mgt_developertoolbar.xml
app/design/frontend/base/default/template/mgt_developertoolbar
app/etc/modules/Mgt_Base.xml
app/etc/modules/Mgt_DeveloperToolbar.xml
js/mgt_developertoolbar

app/etc/modules/AvS_ScopeHint.xml
app/locale/de_DE/AvS_ScopeHint.csv
js/scopehint/tooltip.js
app/code/community/AvS/ScopeHint
app/design/adminhtml/default/default/layout/scopehint.xml

config.codekit
.sass-cache/
.scss-lint.yml
vendor/</code></pre></p>
<p>Damit schließe ich von vornherein aus, dass meine üblichen Entwicklertools (ScopeHint, TemplateHints, DevToolbar) überhaupt im Git auftauchen. Zudem gibt es Dateien meiner Programme, die ich ebenfalls nicht angezeigt bekommen möchte (modman, PHPStorm, CodeKit, OSX).</p>
<h5>Projektspezifische .gitignore</h5>
<p>Anschließend gibt es in jedem Projekt eine eigene <code>.gitignore</code> Datei für Dateien, die sich zwischen Entwickler-, Test- und Produktivumgebung ändern (Zugangsdaten, etc).</p>
<p><pre class="highlight"><code>/app/etc/local.xml
robots.txt</code></pre></p>
<p>Meistens stehen hier natürlich noch weitaus mehr Dateien, aber diese beiden Vertreter sind immer mit dabei.</p>
<p>Für beide gibt es eine Entsprechung im Git selbst: <code>app/etc/local.xml.dist</code> und <code>robots.txt.dist</code>. Die enthalten den Mustercode, mit dem ich dann auf den jeweiligen Systemen das Original erzeuge (siehe später beim Thema Deployment).</p>
<p>Bitte immer daran denken, dass ihr diese Dateien auf den jeweiligen Maschinen separat sichern müsst. Hier bietet euch Git kein Sicherheitsnetz durch versionierte Kopien! Einmal weg -&gt; für immer weg!</p>
<h3>Lokale Serverumgebung mit MAMP</h3>
<figure class="img-responsive alignleft half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/icon-mamppro.png" alt="icon-mamppro"></figure>
<p>Es gibt mittlerweile viele Entwickler, die sich mit Vagrant, Docker und hastenichtgesehen beschäftigen. Wie ich aber am eigenen Leib schon feststellen musste, ist das alles nur dann ganz hipp und witzig, wenn es mit den mitglieferten Tutorials und Skripten aus-der-Kiste-heraus (Anm. d. Autors: out-of-the-box) funktioniert. Geht's aber mal nicht, stehen wir da … ja … und dann? Stackoverflow driven error resolving durch copy'n'paste'n'trail'n'error.</p>
<p>Alles in allem ein echt zeit- und nervenfressender Prozess, der nur in wenigen Fällen zum erwünschten Ergebnis führt.</p>
<p>Einfacher ist es, direkt &quot;bare metal&quot; zu arbeiten. Das hat allerdings auch einige Nachteile, über die man sich im Klaren sein muss. Damit wir lokal überhaupt Magento ausführen können, brauchen wir einen Webserver, einen SQL-Server und PHP. Das alles vereint gibts mit <a href="https://www.mamp.info/en/downloads/">MAMP (Pro)</a>. Die Investition in MAMP Pro (39 €) rentiert sich, wenn man viele Projekte parallel betreibt und sich die Zeit sparen will, die ganzen Domainkonfigurationen (vhosts) händisch zu erzeugen.</p>
<p>Um ehrlich zu sein: MAMP Pro ist jetzt nicht das geilste Teil vor dem Herrn, aber es tut, was es soll. Problematisch sind Upgrades, weil sich bei denen die PHP- und MySQL Konfigrationen in Luft auflösen. Problematisch ist das auch deswegen, weil bei MAMP Pro die Dateien mit dieser Konfiguration in völlig unwegbaren Untiefen des Systems verstecken. Nervig ist es auch, weil manche Konfiguration in plist-Dateien steckt, die sich nur fummelig zusammenführen lassen.</p>
<p>Diese Schmerzchen aber werden durch den Komfort einer lokalen Entwicklungsumgebung und der damit verbundenen Geschwindigkeit meines Erachtens aufgewogen.</p>
<h4>Nachteil von &quot;Bare Metal&quot; Entwicklung</h4>
<p>Normalerweise sind die Projekte unserer Kunden bei unterschiedlichen Hostern untergebracht, die alle unterschiedliche Konfigurationen –&nbsp;ggf. sogar unterschiedliche Betriebssysteme&nbsp;– laufen lassen. Ziel in der Webentwicklung ist es, mit der eigenen Entwicklungsumgebung so nah wie möglich am Live-System zu bleiben. Das ist mit der &quot;Bare Metal&quot; Arbeit quasi unmöglich. Alleine das Betriebssystem bei mir (macOS) dürften wohl bei den wenigsten Hostern so im Einsatz sein. </p>
<p>Immerhin kann ich in MAMP Pro die verwendete PHP-Version weitestgehend selbst einstellen. Mit dem mySQL Server sieht es da aber schon wieder ganz anders aus.</p>
<p>Wir müssen also darauf vertrauen, dass die Bugs und Performanceschwierigkeiten im Live- wie auch Stagesystem mit dem unseren zumindest tendenziell zusammenlaufen. Die Erfahrung zeigt, dass die PHP-Version wenigstens in der zweiten Stelle übereinstimmen sollte bspw: 5.6.21 auf dem Server, 5.6.25 im MAMP Pro und schon ist im Grunde alles gut. Die meisten Bugs sind meist unabhängig von der Serversoftware und treten so oder so auf.</p>
<p>Ein ganz wichtiges Tool, was man für die Magento Entwicklung in PHP auf jeden Fall auch braucht, ist <a href="https://Xdebug.org">Xdebug</a>. In MAMP Pro gibt es dafür zum Glück ein Häkchen. Bitte daran denken: Ist Xdebug aktiviert ist PHP um längen langsamer. Braucht man also für die Arbeit im Frontend keine Xdebug-Unterstützung rentiert es sich, das kurzerhand abzuschalten.</p>
<h4>Xdebug</h4>
<p>Dieses nette Modul für PHP sorgt dafür, dass euer PHPStorm nach drücken des Telefonhörers <figure><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/phpstorm-xdebug.png" alt="phpstorm-xdebug"></figure> in der Toolbar auf ankommende PHP-Prozesse lauscht. Passieren wird dann erstmal nicht viel. Aber sobald ihr in einer PHP Datei einen Breakpoint setzt, <figure><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/phpstorm-breakpoint.png" alt="phpstorm-breakpoint"></figure> wird der PHP-Prozess an dieser Stelle angehalten und ihr habt einen ziemlich präzisen Einblick, was gerade mit eurem Skript passiert:</p>
<ul>
<li>alle vorhanden Variablen und Objekte im aktuellen Scope sind sichtbar</li>
<li>Stack trace der aktuellen Methode (woher kommt der Aufruf, abgehend von index.php)</li>
<li>in Methodenaufrufe hineinspringen</li>
<li>einzelne Befehle ablaufen lassen</li>
<li>beliebigen PHP-Code im Evaluator im aktuellen Scope ausführen</li>
</ul>
<figure><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/phpstorm-xdebug-inspector.png" alt="phpstorm-xdebug-inspector"></figure>
<p>Gerade bei der Arbeit mit Magento hilft es ungemein, zu sehen, welche Objekte gerade mit welchen Eigenschaften (<code>_data[]</code> Array!) verfügbar sind.</p>
<p>Ich habe die Konfiguration von Xdebug etwas angepasst:<br />
<pre class="highlight"><code>xdebug.max_nesting_level = 500
xdebug.var_display_max_children = 512
xdebug.var_display_max_data = 1024
xdebug.var_display_max_depth=5
xdebug.collect_vars=on
xdebug.collect_params=4
xdebug.show_local_vars=on
xdebug.show_exception_trace=off</code></pre></p>
<p>Damit werden ein paar zu restriktive Einstellungen von Xdebug etwas gelockert und die angezeigten Informationen erweitert. Die Konfiguration gehört in eure jeweilige php.ini Datei - bei mir steht es ganz am Ende. Nutzt ihr, wie ich, MAMP Pro, dann schreibt euren Kram bitte immer HINTER die Zeile <code>; DONT REMOVE: MAMP PRO phpx.x.xx.ini template compatibility version: x</code></p>
<h3>SSH-Zugänge</h3>
<p>Früher hat man nur FTP verwendet. Nachdem aber mittlerweile &quot;Gott und die Welt&quot;™ versucht, Daten abzugreifen, empfiehlt es sich, immer eine gesicherte Verbindung zum Webserver zu nutzen. </p>
<p>Das geht wahlweise und unbequem langsam per FTP over SSL oder aber ordentlich und halbwegs professionell via SSH. Beim Zugang via SSH solltet ihr darauf achten, euch statt mit Passwort mit einer Schlüsseldatei am Server anzumelden.</p>
<p>Dazu richtet ihr euch lokal ein Schlüsselpaar ein. Auf der Kommandozeile geht das mit </p>
<p><pre class="highlight"><code> ~/   cd ~/.ssh
 ~/.ssh/   ssh-keygen -t rsa -b 2048
Enter file in which to save the key (/Users/webguys/.ssh/id_rsa):</code></pre></p>
<p>SSH-KeyGen möchte von euch nun gerne einen Dateinamen für das Schlüsselpaar haben. Ich nutze für jeden Kunden ein eigenes paar und daher sind das bei mir dann gerne mal die Kundennamen: <code>webguys</code> zum Beispiel.</p>
<p>Anschließend könnt ihr ein Passwort für den Schlüssel speichern, das ihr immer dann eingeben müsst, wenn der Schlüssel verwendet werden soll. Wer's mit der Sicherheit nicht so ganz genau nimmt, gibt kein Passwort ein. Nachteil: Ergaunert sich jemand eure private Schlüsseldatei, ist der Zugang zum Server quasi offen.</p>
<p>Habt ihr die Schlüsseldateien erzeugt, gibt es eine <code>webguys</code> und eine <code>webguys.pub</code> Datei im <code>~/.ssh/</code> Verzeichnis. Der Inhalt der <code>webguys.pub</code> Datei muss nun auf den Zielserver übertragen werden. Also: SSH mit Passwort zum Zielserver öffnen und dann auf dem Server die Datei <code>~/.ssh/authorized_keys</code> öffnen. Dort hängt ihr den Inhalt eurer <code>webguys.pub</code> Datei einfach an die letzte Zeile an.</p>
<p>Wer's bequem mag, kann sich dann auf dem eigenen Rechner unter <code>~/.ssh/config</code> eine eigene Konfiguration anlegen, die festlegt, wie sich SSH mit euren Zielservern verbinden soll und welche Schlüsselpaare dafür zum Einsatz kommen:</p>
<p><pre class="highlight"><code>host webguys.de
 HostName webguys.de
 IdentityFile &quot;~/.ssh/webguys&quot;
 User riconeitzel</code></pre></p>
<p>Für jeden Server (respektive jedes Projekt) gibt es also so einen Eintrag.</p>
<h3>Wer viel CSS schreibt, freut sich garantiert darüber</h3>
<figure class="alignleft img-responsive half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/sass-logo.svg" alt="sass-logo"></figure>
<p>Bevor wir zum Finale des Deployments an sich kommen, hier noch ein Hinweis für CSS Fetischisten. Es gibt ja mittlerweile für vieles nette Helferlein. So auch für's CSS schreiben. Dazu muss man noch nicht mal unbedingt das volle Potential dieser Lösungen ausschöpfen.</p>
<p>Die Rede ist von CSS-PreProzessoren. Diese Programme können eine etwas leichter zu schreibende Form von CSS in normales Browser-CSS übersetzen.</p>
<p>Beispiel:</p>
<p><pre class="highlight"><code>.body {
    background-color:red;
    font:15px/21px 'Open Sans', Helvetica, Arial, sans-serif;
    #page {
    	width:980px;
    	margin:0 auto;
    }
}</code></pre></p>
<p>Ihr seht hier das Verschachteln von CSS. Ein normaler Browser kann damit nichts anfangen. Nachdem Verwursten dieser Sass-Datei kommt aber schönes CSS dabei heraus:</p>
<p><pre class="highlight"><code>.body {
    background-color:red;
    font:15px/21px 'Open Sans', Helvetica, Arial, sans-serif;
}

.body #page {
	width:980px;
	margin:0 auto;
}</code></pre></p>
<p>Zugegeben, dieses Beispiel ist mehr als dürftig. Mit Sass könnt ihr weitaus mehr machen:</p>
<ol>
<li>Variablen</li>
<li>Berechnungen</li>
<li>Mixins (Mini-Funktionen)</li>
<li>Imports</li>
</ol>
<p>Besonders die Variablen solltet ihr euch noch ansehen. Am Kopf eurer Sass Datei legt ihr eure Standardfarben fürs Projekt fest:</p>
<p><pre class="highlight"><code>$firmenfarbe: #cc0000;
$auszeichnung: #12345a;</code></pre></p>
<p>Später im Code verwendet ihr dann einfach nur noch eure Variablen:<br />
<pre class="highlight"><code>h1 {
	color: $firmenfarbe
}

p.auszeichnung {
	background-color: $auszeichnung;
}</code></pre></p>
<p>Ändert sich nun die Firmenfarbe, ist das für euch ein leichter Handgriff in Zeile 1 und das war's. Genauso können Abstände, Schriften, Größen und vieles mehr in Variablen ausgelagert werden.</p>
<h4>Compiling von Geisterhand</h4>
<figure class="alignleft img-responsive half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/codekit-logo.svg" alt="codekit-logo"></figure>
<p>Damit das ganze dann auch von alleine in echtes CSS umgewandelt wird, braucht's natürlich den PreProcessor. Sass kann man sich wahlweise auf die Kommandozeile packen (gut für die Git-Berserker) oder sich die Sass-Datei mit nützlichen GUI-Tools, wie z. B. <a href="https://codekitapp.com">CodeKit</a> kompilieren lassen. Dabei kommt CodeKit mit eigenen Compilern zur Tür hereinspaziert und man spart sich die separat manuelle Installation der ganzen Tools. Sozusagen: All-in-One!</p>
<figure class="alignleft img-responsive half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/codekit-config.png" alt="codekit-config"></figure>
<p>CodeKit ist nicht alleine für das Kompilieren von Sass zu CSS zuständig, sondern beherrscht auch alle anderen gängigen Sprachen wie Less, Stylus, CoffeeScript, Markdown, … und sorgt mit einem Watcher dafür, dass eure Zieldateien immer <strong>dann</strong> aktualisiert werden, wenn sich die Quelldateien verändern. Und mit etwas Einstellungen hier und etwas Konfiguration da kann CodeKit sogar euren Browser immer neuladen, sobald sich die Zieldateien geändert haben.</p>
<p>Nützlich finde ich, dass sich je Datei bestimmte Einstellungen speichern lassen - bequem über die GUI eben. Dazugehört der Zielpfad der fertig kompilierten Datei, aber auch die zusätzlichen Optionen der Prozessoren. Außerdem bieten beispielsweise die CSS Prozessoren in CodeKit die Möglichkeit, direkt im Anschluss mit <a href="https://github.com/postcss/autoprefixer">Autoprefixer</a> und <a href="http://blesscss.com">Bless</a> die CSS-Dateien zusätzlich anzureichen und bugfest für den IE zu machen.</p>
<h3>Wer gerne mit Kunden arbeitet</h3>
<p>Am Anfang war das Telefon. Und der Entwickler sah, dass es scheiße war. Warum? Zum einen werden wir ständig aus der konzentrierten Arbeit gerissen; Außerdem schreiben wir uns ja doch nur die Hälfte auf, und davon die Hälfte auch noch falsch und am Ende geht sowieso der Zettel verloren.</p>
<p>Besser ist es dann schon, sich die Änderungswünsche per eMail schicken zu lassen. Dann kann man wenigstens mit dem INBOX-ZERO Prinzip arbeiten. Aber bei einer entsprechenden Anzahl an Kunden und vorrangig kontinuierlicher Shoppflege wird auch <strong>das</strong> unübersichtlich und man weiß am Ende nicht mehr, wem man welchen Aufwand weitergegeben und was welche Priorität hat.</p>
<figure class="alignleft img-responsive half"><img src="https://www.webguys.de/content/2-magento-1/20161212-schmalspurentwicklung-mit-magento-1/bitbucket-logo.svg" alt="bitbucket-logo"></figure>
<p>Was hilft, sind Ticketsysteme. Zu Beginn - und das ist sehr empfehlenswert - haben wir unsere Aufgaben in <a href="https://confluence.atlassian.com/bitbucket/use-the-issue-tracker-221449750.html">BitBucket Issues</a> organisiert. Man ist nicht wirklich frei in der Konfiguration der Ticketabläufe, aber es hilft einem ungemein, den Überblick zu behalten und mit dem Kunden dokumentiert in Kontakt zu stehen.</p>
<p>Für größere Aufgaben möchte man dann irgendwann auf <a href="https://www.atlassian.com/software/jira">JIRA</a> wechseln. Früher wollte ich JIRA mit der Kneifzange nicht anfassen, weil es völlig überladen und viel zu komplex zu administrieren ist. Jetzt arbeiten wir seit gut einem Jahr mit JIRA und liebe ich es heiß und innig. Ich kann ohne dieses System eigentlich nicht mehr arbeiten! Und auch unsere Kunden kommen ziemlich gut damit zurecht.</p>
<p>Nachdem ich nun gefühlte 24 Seiten nur über die Tools gesprochen habe, bleibt ja nun schlussendlich noch die Frage:</p>
<h2>Wie spiele ich jetzt mein Projekt aus?</h2>
<p>Wir sind da recht pragmatisch: Wir nutzen Git auch auf den Servern. Wir sorgen zuerst dafür, dass jede Website des Kunden wenigstens in einem eigenen DocumentRoot liegt: meist sowas <code>~/html/webguys.de/</code> und führen dann dort ein <code>git clone …</code> aus. Auf dem Staging-System sollte noch ein <code>git checkout develop</code> erfolgen, damit hier nicht der Master Branch vom Live System sondern der aktuelle Entwicklungstand angezeigt wird.</p>
<p>Damit wir bei der Arbeit mit git auf dem Server nicht ständig mit Zugangsdaten hantieren müssen, erstellen wir auch auf den Kundenserver immer Schlüsselpaare. Den öffentlichen Schlüssel (.pub) des Servers hinterlegen wir bei Bitbucket als Deployment Key.</p>
<p>Diese Deployment Key Zugänge haben keine Berechtigung, Änderungen zu speichern, sondern können lediglich lesend auf das Repo zugreifen. Daher werden diese Zugänge auch nicht im kostenpflichtigen Plan angerechnet.</p>
<p>Wichtig ist nun, dass das .git Verzeichnis nicht über den Browser aufrufbar sein darf. Das sollte also Teil eurer .htaccess Datei im Magento-Verzeichnis, besser noch Teil der vHost-Config von Apache sein (die man bei den meisten Hostern für gewöhnlich aber nicht ändern kann).</p>
<p><small><strong>Haters gonna hate:</strong> Ich weiß, dass man das .git Verzeichnis auch woanders ablegen kann, aber es geht in diesem Artikel explizit um die Schmalspurlösung.</small></p>
<p>Unsere Standardeinträge für den Anfang der .htaccess sind diese:</p>
<p><pre class="highlight"><code>RedirectMatch 404 ^/\.git.*$
RedirectMatch 404 ^/Vagrantfile
RedirectMatch 404 ^/puphpet
RedirectMatch 404 ^/var/.*$
RedirectMatch 404 ^/app/.*$
RedirectMatch 404 ^/dev/.*$
RedirectMatch 404 ^/downloader/.*$
RedirectMatch 404 ^/includes/.*$
RedirectMatch 404 ^/lib/.*$
RedirectMatch 404 ^/pkginfo/.*$
RedirectMatch 404 ^/shell/.*$</code></pre></p>
<p>Ist das Repo erfolgreich ausgecheckt und erlaubt auch keine Zugriffsmöglichkeit auf den .git Ordner, müsst ihr eure <code>local.xml</code> einrichten. Dazu in <code>app/etc/</code> die am Anfang mal angelegte <code>local.xml.dist</code> nach <code>local.xml</code> kopieren und die Zugangsdaten der Datenbank anpassen.</p>
<p>Anschließend ladet ihr euren SQL-Dump in die Datenbank des Servers. Achtet hierbei auch unbedingt darauf, für die folgenden <code>path</code> Einträge in der <code>core_config_data</code> Tabelle, die Domain zu korrigieren:</p>
<p><pre class="highlight"><code>web/unsecure/base_url =&gt; http://www.webguys.de/
web/secure/base_url =&gt; https://www.webguys.de/
web/cookie/cookie_domain =&gt; .webguys.de</code></pre></p>
<p>Anschließend Caches löschen, Indizes aufbauen und fertig!</p>
<h3>Nützliche Aliase für die Shell</h3>
<p>Die Standardaufgaben wie Reindizieren, Cache löschen und Sessions löschen erledigen bei mir 4 Aliase, die ich in meiner ~/.bash_profile Datei hinterlegt habe:</p>
<p><pre class="highlight"><code>alias cache='rm -fr ./var/cache/* '
alias magelog='tail -f ./var/log/\*.log'
alias reindex='php_cli -f ./shell/indexer.php reindexall'
alias session='rm -fr ./var/session/\*'</code></pre></p>
<p>Wichtig: cache und session bitte <strong>immer</strong> im Magento Root ausführen!</p>
<h2>Projekt aktualisieren</h2>
<p>Wenn ich jetzt fleißig war und auf dem Test- oder Livesystem Änderungen ausspielen will, logge ich mich per SSH auf der Machine ein und rufe lediglich</p>
<p><pre class="highlight"><code>git pull
cache</code></pre></p>
<p>auf und habe sofort das Ergebnis im Browser.</p>]]></description>
    </item>
    
  </channel>
</rss>