CakeDC Blog

TIPS, INSIGHTS AND THE LATEST FROM THE EXPERTS BEHIND CAKEPHP

Best CakePHP Plugins

Members of our team had the privilege of helping with CakeFest 2020 this year. One added virtual feature was the giveaways from CakePHP, these were done in the form of fastest-to-answer, trivia, or participation (random draw).  One of the giveaway games was to share your favorite CakePHP plugin but like, how do we only pick one, right? Anyway… There was a lot of participation in this giveaway! A few people even named our CakeDC Users plugin as their favorite *cue blushing face*. But in all seriousness, I thought it would be a good idea to share with you some of the plugins that were named most useful/helpful by CakeFest attendees this year….   Like I mentioned, the CakeDC users Plugin: https://github.com/CakeDC/users Queue Plugin: https://github.com/dereuromark/cakephp-queue Bake: https://github.com/cakephp/bake DataTables: https://github.com/fheider/cakephp-datatables CakePHP-tools: https://github.com/dereuromark/cakephp-tools Authentication: https://github.com/cakephp/authentication CakePHP-image: https://github.com/josbeir/cakephp-image Fixturize: https://github.com/lorenzo/cakephp-fixturize CakePHP File-Storage: https://github.com/burzum/cakephp-file-storage Crud: https://github.com/FriendsOfCake/crud IDE Helper: https://github.com/dereuromark/cakephp-ide-helper Asset-Compress: https://github.com/markstory/asset_compress CakePHP Debug Kit: https://github.com/cakephp/debug_kit Plum-Search: https://github.com/skie/plum_search CakePHP API: https://github.com/cakedc/cakephp-api/ Bootstrap UI: https://github.com/friendsofcake/bootstrap-ui Trash: https://github.com/usemuffin/trash   You can check out the full list of CakePHP Plugins at Plugins.CakePHP.org.  Have you been utilizing these tools? If not, it may be a good idea to start… while each of these serve their own purpose, using all of them can make your baking process a lot easier.    Perhaps your favorite didn’t make this list? Tell us about it… email HERE. Or, tweet us @CakeDC, @CakePHPThanks for baking!  

CakeFest 2020 recap

Taking a deep breath….. We have made it through another successful CakeFest event.    We didn’t know exactly what to expect with a fully virtual event, as it opens the door for a list of things that can (and most likely will) go wrong. Would the speakers show up? Would the internet connections keep things from running smoothly? Would attendees enjoy the information?   The answer to all of those questions is yes.    The lineup this year was amazing, and we had speakers from 6 different countries! With the ever changing way of life, our team wanted to have a diverse group of speakers, with some talking about some pretty serious subjects - like Dr. Jennifer Akullian and the mental health issues faced in the tech world. Jen allowed for questions and how-to-handle scenarios, and worked through some tough issues with attendees. Workshops from Mark Story, Jorge Gonzalez, Mark Scherer and Jose Lorenzo provided incomparable information while building projects in real time. All of the talks provided useful information that we hope all attendees will take with them as they bake in the future.    Now, does all of this bragging mean we didn’t have any issues? No. As I said in our opening, I think our group is pretty awesome and perfect, but the fact of the matter is, no one is. When bringing people together from all over the world, it is hard to test and check every connection multiple times. We had our fair share of internet lag, connection problems and audio issues. You know what we did? We worked together, fixed it, switched around the schedule and made it happen.   Virtual CakeFest 2020 was a great success, and exceeded our expectations. We were able to gather bakers from all over the world, in real time, and host a live event! I couldn’t believe how interactive the audience was, and everyone is still baking together in our #cakefest chat channel. I hope everyone is as impressed with the turn out as our team is. I know what you’re thinking… when will the recordings be released. We are working on uploading, editing and releasing the videos ASAP. While we tried to find the best universal timezone, we understand that other obligations kept some from attending, and we want to share these videos with you quickly, so know that we are working on it.    To our attendees: THANK YOU. Thank you for joining, supporting our framework and keeping CakePHP alive.   

I would like to thank all of our speakers:

  Remy Bertot  Junichi Okuyama Mark Story Jorge Gonzalez Jose Lorenzo Mark Scherer Jose Gonzalez Cal Evans Ed Barnard Jennifer Akullian Mariano Iglesias Chris Nizzardini Juan Pablo Ramirez
 

A HUGE THANK YOU TO OUR SPONSORS:

   Cake Development Corporation
  Passbolt 
  Marks Software
  RingCentral  


  Here’s to planning next year’s event to be even bigger and better!  

How to take a rest in five minutes

There are a lot of toys these days. Let me show you one very simple and powerful brick for your next revolutionary invention. Let's assume that you're either:

  • A curious person, and you like to play with things you don't know, just for the case
  • A frontend developer with no PHP coding experience, and your javascript components could use some dynamic backend json resource. Maybe you want to do this without playing with all mocking options or Node.js modules out there...and you don't want to set up MySQL database, user, grant permissions and all that stuff
  • building dynamic user interface, and your code needs to be aware of continuously changing structure of databases using unpredictable table names
  • A creator of a wireless network of IoT devices or swarm of flying educational robots. Or, looking for some stable and performant data backend running on your central Raspberry Pi Zero W with tmpfs mounts and ridiculously huge memory card, while sniffing around for bluetooth signatures
  • aware of services like ngrok and wanting to consume payloads of some IFTTT webhooks locally. Or, allowing a friend with Postman to access and/or feed up your database created (or temporarily exposed) just for that reason.
  • Having a rich html application and flirting with the idea of full blown CakePHP backend running in kiosk mode of the DraftCode application right on your iOS device
  • able to pack a PHP application as a single file Phar archive. For example: to simplify all sorts of domain logic/responsibility distribution across bazillions of machines spawned on demand and utilizing jq in your provisioning scripts
  • praying for headless access to any database that your CakePHP application is able connect to, bypassing implemented validation, callbacks, events, and even sneaking through your authentication/authorization backdoor if you want to
  • going to try following example right away, or at least very soon
If your machine meets the CakePHP Requirements, and you have composer in your $PATH then we are good to go for a CakePHP application using SQLite database accessible through REST. This would have obvious api goodies like CRUD, CORS, HATEOAS, versioning, list, describe, relations, sorting and pagination included by default.

Clock is ticking, time to rest with some book(s)

I don't know how much time of the rest limit we lost on the preface (quick learners are quick readers), but don't worry, unless you are on the slow internet connection and packagist is taking a break - we are almost done. We have a clear picture of what we're going to create, so the rest is a piece of cake.
  • create a CakePHP 4.x project named rest with CakeDC/Api plugin unlocked and loaded:
composer create-project \ --prefer-dist --no-interaction \ cakephp/app:~4.0 rest cd $_ composer config \ minimum-stability dev composer require \ cakedc/cakephp-api:~8.0
  • Unlock and load! Auth bypass, "allow all" switch (don't try this at home)
cp vendor/cakedc/cakephp-api/config/api_permissions.php.default \ ./config/api_permissions.php bin/cake plugin load CakeDC/Api
  • manually (meh, we should be resting now) configure Database.default connection in the file config/app_local.php
'database' => TMP . 'rest.sqlite', 'driver' => 'Cake\Database\Driver\Sqlite',
  • create example table books in the database and start local server
bin/cake bake migration create_books \ title:string:unique \ description:text \ price:integer:index \ available:boolean:index \ created \ modified bin/cake migrations migrate bin/cake server Open http://localhost:8765/api/books in your browser to see the (empty) list of your books, then pick some RESTful Route and take a rest from the back end, even without backend coding, scratching who knows where, etc. You maybe have some SQLite databases laying around, and that would be also worth a try. I forgot to remind you to stop that 300s timer, sorry. You will get lost in time as well, once you'll realize what all is possible with the CakeDC/Api plugin using little to no configuration, and what dimensions are opening to you. If you combine its powerful multilayer services approach with solid implementation of your custom ORM classes embraced by CakePHP core and conventions, the results are amazing. Oh, and a quick note for CakePHP 3.x developers - we like you, but please take your time to upgrade your toolset. Feel free to ask us for help, or use following warranty-limited commands: composer create-project \ --prefer-dist --no-interaction \ cakephp/app:^3.9 rest composer require \ cakedc/cakephp-api:~7.0 bin/cake plugin load -b -r CakeDC/Api

What's next

CakeFest 2020 of course. I hope you all are as excited as we are, together in these hard times. Be safe and take care, folks! And rest too, even if it's a quick one :-)

CakeFest Insider

In case we haven’t reminded you enough lately, CakeFest 2020 is less than a month away. While we have recently hosted a few virtual meetups, a fully virtual conference is uncharted territory for our team. I like to look on the bright side of things, so I will say how excited I am that bakers from all over the world will be able to join in and participate this year. Obviously, with international travel, this hasn’t always been the case for individuals. So my last CakeFest blog, I went into details of what to expect, and how the conference will (hopefully) run - smoothly. However, we’ve had a lot of emails and interest in hearing about what topics will be covered. Our lineup is excellent this year - so we wanted to share some things to look forward to:  

SPONSORS

First, we have to give a shout out to this year’s sponsors Cake Development Corporation  Passbolt Mark’s Software  RingCentral Companies that support Open Source are essential for our communities to move forward and grow!   

SCHEDULE

Day 1 will feature our popular workshops. These sessions are different than normal conference talks because they are basically training sessions to help grow your recipe book. You can see examples of previous years HERE. There will be basic workshops, as well as advanced during the full day session. We have condensed this year’s duration, so what is normally 2 days of workshops will be done in 2 hour sessions by each baker.  Mark Story, Jorge Gonzalez, and Jose Lorenzo are back. This year, we’ve also recruited another core member, Mark Scherer… you may be familiar with Mark from hosting many of our virtual meetups.  Topics that our core members MIGHT include are (these are being discussed): CakePHP 4.x, as well as project examples -  a repost builder, a headless micro cms, a media server to upload/download cache files from S3. The speaker’s will build through a project with you, answering questions as they progress.  Have an idea you’d like the workshops to include? Email us! Cakefest@cakephp.org.
  Now, day 2. This year, we decided to go ahead and try a few new things. Of course being 100% digital, condensing times, and some topics and speakers that may be unexpected. I personally invited some speakers that I thought could bring something fresh to the tech conference table.  One of these being OSMI (Open Source Mental Illness). I wanted to bring mental health importance to light, and this group is doing amazing things for the development world. Mental health doesn’t have to be a taboo subject. Dr. Jennifer Akullian will be talking about mental health, stress, and burnout in the tech industry. In addition to an overview of the research, distinctive considerations of the industry we work in will be discussed, as well as how to improve the management of common stressors in technology during an incomparable time in the world. We will also hear how some people got their start, like Michael Hoffman, and learn how to build solid architecture with CakePHP Plugins thanks to Ed Barnard. Ever wondered how to release your own plugin? Jose Gonzalez will shed some light. We are also delighted to have one of our involved Japanese community bakers, Junichi Okuyama, joining us as a speaker this year talking about helpful tips that he has learned for baking with CakePHP. Our keynote will be given by our diamond sponsor representative from Passbolt, Remy Bertot.  Other talks will include: well known podcast host Cal Evans talking about all of the cool things that can be done with PHP's built in DateTime math, Mariano Iglesias,  core members Mark Story, Chris Nizzardini and Mark Scherer. Popular contributors Juan Pablo Ramirez and Nicolas Masson will join us and share more details about CakePHP fixture factories.   You can see the full schedule HERE, and plan your attendance accordingly. 
 

SLACK CHATS 

We will also have slack chat rooms for attendees. This will give everyone the opportunity to interact with other watchers as well as speakers. Questions are welcomed before, during, and after the event. We will have team members monitoring the chat and trying to get all questions answered as soon as possible.   Slack channel tag: #cakefest  So basically, have a coffee and a chat with other attendees and presenters.    Have you purchased your ticket yet? WHAT ARE YOU WAITING FOR?    

Upgrading to CakePHP 4

As you know, CakePHP announced the version 4.x last December.I recommend that you consider upgrading your applications to the next version, to keep up to date and get all the benefits. Now, let's see how to bake!  

Step 1: Upgrade PHP

First things first, if you are not running on PHP 7.2 or higher, you will need to upgrade PHP before updating CakePHP. CakePHP 4.0 requires a minimum of PHP 7.2.  

Step 2: Upgrade Templates and Resources

There is an upgrade CLI tool for rename and moving the templates and resources:   Templates and Resources must have been moved and renamed, check the result below: * This project doesn't have Resources files   Now, let's create a new constant for Resources on /config/paths.php: Finally, update the paths on config/app.php:  

Step 3: Upgrade CakePHP

The next step is optional (and the Migration Guide included this) - run the rector command to automatically fix many deprecated method calls: The rector applied on codebase some return type declarations: https://github.com/rafaelqueiroz/cakephp-upgrade-sample/commit/d7e5c2ecc5dc28045700a270721f07098a8e189c?branch=d7e5c2ecc5dc28045700a270721f07098a8e189c&diff=split Pay attention: It is important to apply rector before you upgrade your dependencies.   Upgrade CakePHP and PHPUnit: PHPUnit can be upgraded easily. Most of the time, the --update-with-dependencies doesn’t work with me for CakePHP: The root of the issue is the packages using Caret Version Range, so let’s update debug_kit, migrations and bake using editor:   Here we go:   Now, let see how the project looks: Here, we have few deprecations and warnings. Do you remember I mentioned the rector is optional? So, the question is the rector and it's not always able to handle these issues.   I will use the PHPStan to fix this - we will install with composer: Now, we can run the phpstan analyse and fix the issues:   It's up to you how much effort you will put in with PHPStan issues. I recommend fixing everything. For this post, I did fix only what was needed to run the project after the update, you can check the fixes on this commit.   After the last fixes, the project is running well:  That’s all? No. But we upgraded CakePHP? Yes. Real applications probably use many plugins, and if these plugins don't have a version for CakePHP 4, you will need to update. Depending on the size and level of complexity of the project, the upgrade could be hard, but never impossible.    If you do not feel confident or your company would like to outsource support for this, don't hesitate to contact us at Cake Development Corporation. Our team is offering a full upgrade from CakePHP 2/3 to CakePHP 4. This will be a migration of your current application code to make it compatible with CakePHP 4 features, plugins, security settings, etc. We will be doing these migration services for a special rate - something we have never done before! Learn more about our Upgrade Services You can check the codebase of the examples on this repository. The branch upgrade has all steps by commit.  With every release CakePHP gets better, and version 4.x is no exception. There are many benefits that come with upgrading, and it makes baking a lot easier.

Using Postgres as default database- hi...

SQL language for different databases has some differences, which could cause problems after migrations between these databases. Here, we collected some hints, problems could appear during migration from MySQL to PostgreSQL. We focus on the CakePHP model layer and query generation layer.  

Tables join in where expression

Often we want to join two tables using a condition like $query->where([‘Author.id = Article.author_id’]) which works fine till we dont need field alias quotes. In the case of migration to postgres, we might want to enable autoQuotes. In this case, we can use $query->newExpr()->equalFields(‘Author.idArticle.author_id’).   

Case sensitivity in like expressions

By default mysql does case insensitive search. Switching to postgres, you can note that some functionality works differently. Hopefully you have tests, which covers your code, and this will be detected during migration. Postgres uses custom syntax for such queries named ILIKE.The case of old style conditions where method arrays straight forward,  you’d just go with ILIKE instead of LIKE. But what if we want to use ILIKE in builder methods... Here is that example:     return $query->where(         function (QueryExpression $exp) use ($field, $value): QueryExpression {             return $exp->add(new \Cake\Database\Expression\Comparison($field, $value, 'string', 'ILIKE'));         });   

Type casing

As postgres is much more strict with types, type casing is not a rare operation and may be needed. Here is an example of how to perform it using FunctionExpression$expr = (new FunctionExpression('CAST'))->setConjunction(' AS ')->add([$id, 'varchar' => 'literal']); which generates expression like :id AS varchar there :id is the placeholder for variable $id. This trick, used with literal, allows you to cast to any postgres type.  

 Quotes of tables and fields

Sometimes it is critical to inform CakePHP that the field should be quoted. One recommendation is to avoid using plain strings in case of table joins, or using IS NULL as string. So if array syntax is used, all CakePHP conventions must be followed. However,  sometimes we should help the ORM and obviously wrap a field name with IdentifiedExpression. Let's take a look back to the previous example, but now we want to type cast not value, but a field. The only solution to quote field name correctly is using this code:     $id = new IdentifierExpression($this->aliasField($field));     $expr = (new FunctionExpression('CAST'))->setConjunction(' AS ')->add([$id, 'varchar' => 'literal']);   

Building complex arithmetic expressions

In case we want to generate expressions in query fields, and we don’t want to overcomplicate logic, we could use these next tricks. Here, I have created ListExpression, which could be used as a collection of expressions. Each of these are corrected, and generates a query with correct handling of each element. See: https://gist.github.com/skie/f6e4f1a1b61e0f902a507f7907c3bbf2 So, say we want to generate expressions like this: “Events”.”time_to” - “Events”.”time_from”... With ListExpression, it can be done quite easy:  $diff = new ListExpression([new IdentifierExpression('Events.time_to'), '-', new IdentifierExpression('Events.time_from')]);   Hopefully these tricks will be as useful for your baking as they have been for mine! 

CakePHP Common Errors: Saving HasMany ...

The Cake Development Corporation team performs many code reviews. In fact, that is our starting point with every new client, as we offer free quick reviews. This is a good way to see where code stands, and how much work will need to be done to get it functioning properly.  One of the common errors we have found while doing Code Reviews of existing applications or just working with inherited code, it’s the way HasMany relations data is saved.  We have noticed that to save HasMany relations, some developers save the target relation, and then when the ID is retrieved after saving, they save one per one each item of the ‘many’ relation. There is no need to do this, as CakePHP can do all this in one single ‘save’! You won’t have any issue related to inconsistent data, because everything will be stored in one single transaction and your code will look much more clean. Let’s see a quick and easy to follow example - We will have the following relations: ‘Users’ and one User could have many ‘Addresses’.  We wish to save one user and this user will have two addresses.  First, you need to build the form in the proper way, the request data should follow the structure of your entities. The key in the form is the fieldName for the hasMany inputs. They must follow this format: {entityname}.{position}.{property}, for example: adddress.0.street_1, adddress.0.street_2, etc for the first item so store, for the second one: : adddress.1.street_1, adddress.1.street_2, and so on. More examples can be found here:  https://book.cakephp.org/4/en/views/helpers/form.html#creating-inputs-for-associated-data.
<?= $this->Form->create($user) ?> <fieldset>    <legend><?= __('Add User') ?></legend>    <?php        echo $this->Form->control('first_name');        echo $this->Form->control('last_name');        echo $this->Form->control('phone');    ?>    <legend><?= __('Address 1') ?></legend>    <?php    echo $this->Form->control('addresses.0.street_1');    echo $this->Form->control('addresses.0.street_2');    echo $this->Form->control('addresses.0.zip');    echo $this->Form->control('addresses.0.city');    echo $this->Form->control('addresses.0.state');    ?>    <legend><?= __('Address 2') ?></legend>    <?php    echo $this->Form->control('addresses.1.street_1');    echo $this->Form->control('addresses.1.street_2');    echo $this->Form->control('addresses.1.zip');    echo $this->Form->control('addresses.1.city');    echo $this->Form->control('addresses.1.state');    ?> </fieldset> <?= $this->Form->button(__('Submit')) ?> <?= $this->Form->end() ?>
Now that we have the form, we need to convert the request data. The Table class provides an easy and efficient way to convert one or many entities from request data. It’s needed to define which associations should be marshalled, using associated
public function add() {    $user = $this->Users->newEmptyEntity();    if ($this->request->is('post')) {        $user = $this->Users->patchEntity($user, $this->request->getData(), ['associated' => ['Addresses']]);        if ($this->Users->save($user)) {            $this->Flash->success(__('The user has been saved.'));              return $this->redirect(['action' => 'index']);        }        $this->Flash->error(__('The user could not be saved. Please, try again.'));    }    $this->set(compact('user')); } In this example we are saving one user and two addresses for the given user.  Associated data is validated by default, If you wish to bypass data validation, pass the validate => false option, for example: $this->Users->patchEntity($user, $this->request->getData(), ['associated' => ['Addresses' => [‘validate’ => false]]]).    We are all about working smarter and in less time, so I hope this information will be useful! Take a look here for more information: https://book.cakephp.org/4/en/orm/saving-data.html#converting-request-data-into-entities  We will be posting more Common Errors while using CakePHP.  Keep checking back!

Virtual CakeFest 2020

There we were… just 8 months ago, bright eyed and planning our annual CakeFest event for 2020. The community picked Los Angeles for a host city, and who doesn’t love LA? Our team was excited! Now, fast forward, and we are planning our virtual event. After sitting in on a few well executed virtual events this year, I was pleasantly surprised and impressed. There are still ways to keep these conferences interactive, but theres no denying that it is hard to beat the face to face communication within our CakePHP community, like we have experienced in the past.  Wondering what our (and many others) event will look like this year? Let’s see….  

Virtual

Duh. We will be hosting for attendees all over the world. This is probably the biggest pro about doing digital conferences. Attendees are able to join in, no matter where they live. With international travel, there are many things at play, schedule, finances, family, work. And it is not always an option to attend… The fact that ANYONE can tune in from ANYWHERE is so refreshing. We usually host a 4 day event, but this year we have condensed all of the fun into 2 days, to make things easier on those who don’t want to miss anything. I have had many people reach out to me saying that this will be the first CakeFest that they get to attend live (because we do always post the event after the fact or stream).  Basically, come as you are, dress comfy, and grab the coffee. You get to watch and learn from the comfort of… anywhere you want.
 

Speakers

The same thing goes for speakers - except that they will actually have to wear pants because they will be seen on camera. Our speakers will not have to worry about travel or accommodations, and can fit us in their busy schedules. We have some pretty great talks lined up this year, from building plugins, to mental health in tech, theres something that all attendees can benefit from.  As always, we will have our workshops as well, one FULL day of basic to advanced CakePHP workshops, which is the highlight of the event for most developers. The schedule is posted on our site, but there could be unexpected changes or additions moving forward.  Times are ever changing, am I right? We will allow for quick breaks throughout the events to allow potty time, refills, walking the dog etc.   

Interactive

We will have the opportunity for Q&A during talks, as well as a “convos with speakers” chat available for attendees to interact with the spealers during the event. On RingCentral, there is a fully interactive chat rolling through the presentations. The speakers are on a pretty strict time schedule, but will get to any questions they can. For this reason, we are creating chat rooms for post talk Q&A as well.  Basically you can have a chat with any available speaker for more insight on their presentations, to ask them questions, or just say hello!  This access will be sent to attendees via email before the live event. As I mentioned, our schedule is posted, and we are even taking questions ahead of the event. If there’s something you’d like to request the speaker includes, or get ahead of the question game, you can send us an email HERE.  

Giveaways

Need I say more? This year we have decided to give away some pretty cool digital prizes during the event. This may be in the form of trivia, or a game of “who can tweet us the fastest”. Either way, everyone attending will have the chance to win! If you don’t follow us on social media, you may want to start, because there MAY or may not be some pre event giveaways as well.   

Tickets

Tickets are available now. They are limited, so make sure you purchase & register ASAP. Get them here: CakeFest.orgWe hope to “see” you at CakeFest 2020! October 8-9.   

Why Use CakePHP

CakePHP  is a web development framework running on PHP. CakePHP provides tools to help build websites and web apps faster, stable and very easy to maintain. We will outline some of the interesting features of the CakePHP framework below:

Authorization

The authorization layer is in charge of determining if a user is able to access a feature or not. Authorization in CakePHP may be as complex as you want. It is powerful because you can define permissions per role, ownership, or anything else by just writing a policy.  In CakePHP 4, the Authorization layer is part of another package, which means that it can be used in a non-CakePHP app.

MVC Support

The layers in CakePHP are very explicit. Firstly, you will see that the application has specific folders for each layer (Model, Controller, Template). Secondly, you are encouraged to not access layers incorrectly, because the right way is simple enough. After using multiple frameworks out of there, I can say that CakePHP implements MVC and it implements it well.

Bake

Bake is not something new in CakePHP 4. It has been included with Cake since version 0.1.0 and even now when it is released as a plugin, it is a required tool for any developer. Bake makes generating an application, controllers, models and everything else easier... just running a command and within minutes. Other frameworks may have some tools, but in my opinion, there is nothing like bake.

Database Migration

Database migrations with CakePHP are simple, quick and safe. Those are probably the only three things you look for when versioning your database. MigrateShell can be used to generate migrations and you are able to alter database structure in any way, as well as running queries/commands. The CakePHP team is also responsible for Phinx plugin development.

Multi Language Support

This is another old feature that has been in CakePHP for years and has been recently improved in CakePHP 4. I must say that we have not found any other framework with the same set of features for internationalization and localization. You only need to use the translation function for any strings [__()] and define a way to set the language in your app. After doing that, just extract PO files using the shell and start translating your strings.

Powerful ORM

ORM in CakePHP 4 is able to do anything you can imagine. Simple and complex queries have an easy way to be executed using ORM functions. Joins, grouping, subqueries can be done in just minutes. Even after working in complex reporting for several clients, we have not found something that cannot be done through CakePHP ORM.

PHP Standards 

Going back to previous CakePHP versions (1.x and <2.x) we found that they implemented their own standard, which was good if you only worked with CakePHP... but it made integrating external libraries more difficult. In CakePHP 4, as well as 3.x, it is not a problem anymore because the wonderful CakePHP team is aware of the latest standards for the language and they are implemented inside the core... as well as the libraries released around it. It allows you to use any PHP library or package inside your Cake application.  I could go on all day about features that I like about the CakePHP framework, but there's more blogs to write in the future. I hope this gives you some incentive to use it.  

Managing A Company During A Global Pan...

A worldwide pandemic is not something a company, a manager, or a team ever plans for. This time 6 months ago we were bringing in more clients than anticipated, and planning for an international conference. Fast forward to now, just like most companies we have been hit, our conference has gone virtual, and many employees are still worried about what is to come.  Here are 5 things I have learned during these uncertain times:  

1. Don’t panic. 

Easier said than done, right? Being responsible for a team of great people and their financial, as well as professional, well being can be trying at times. I have learned it is best to stay calm, push forward and still do the best we can, even when our best isn’t always enough. Luckily, I am not a worrier by nature, and I hope that I can be a solid backbone for my team and clients, while letting them know that they (and our company) are my top priority … now more than ever.   

2. Be transparent

It is best to have sure answers and knowledge of what is expected, and to be open and honest about this with the team. If we are going to be working longer hours one week, maybe shorter the next, I want to be upfront so that no one is caught off guard. If policies or procedures are changing, they are notified immediately   Same thing goes for our clients, we have always prided ourselves on being honest and transparent about the behind the scene scenarios. It may not always be good news that’s delivered, but it will be honest. I have set goals to make my expectations clear, and reasonable.   

3. Be available:

Someone like myself, I am always going in a million directions. I have made it a point to make myself available for help, support, or whatever it is that someone may need. This goes for  clients, team members, even friends.  A pandemic like this really makes you step back and think about what is important, and things that you may not have made priorities in the past that needed to change. Our team is used to working remotely, but we communicate daily, and we always have open lines of communication (sometimes in the middle of the night as we all work in different time zones.  

4. Be Understanding

If 2020 has taught us anything, it’s to expect the unexpected. It is vital that each individual understands that not everyone is the same. We do not handle stress the same, we go through trials and tribulations differently, so it is important to be empathetic. We need to provide tools for success... and sometimes that means paid time off, new communication platforms, team building exercises, or just listening and being compassionate. I think a mistake a lot of people made early on was expecting everyone to adjust to the new way of life, with no clear direction. This resulted in a lot of confusion, and negativity, instead of learning together and changing the course of action. 
 

5. Surround yourself with a great team. 

Luckily for me, our team is fully functional without me, maybe that should scare me a little, huh? We have structure, we built up trust with each other, and everyone works towards the same goal - being successful, delivering to our clients, and growing together. While it may be my job to keep everyone moving forward and be motivating, it’s no secret that they motivate me, too. Despite working in different countries, our team has built great relationships with each other, and everyone is ready to step in and help their colleagues whenever necessary. 
 

Final thoughts

Has 2020 been different than I imagined? Absolutely. We do not know the answers to every question. We also do not know where this year may take our team, the company, or the world! One thing I do know is we will adapt, adjust and keep pushing forward. We will keep providing the best service to our clients as we always have, and we will not panic…. Not yet at least. 
 

A Quick CakePHP Local Environment With...

CakePHP and Docker

We all know that while developing a CakePHP software, we need to have a local environment with PHP, HTTP Server (nginx, apache) and a database (MySql, Postgres, Mongodb, etc). Installing those tools directly to your system is the basic way, but it can become a bit tricky when we have multiple projects using different versions of those tools... that’s where Docker will help us. In this article, we will show a quick docker setup to improve our CakePHP local environment. If you don’t have docker installed go to: https://docs.docker.com/get-docker/. It is available for Linux, MacOS and Windows. For our setup we are going to use PHP, Nginx, and Mysql. All of the information required will be added to a new file named docker-compose.yml. In our environment we will need two docker images [https://docs.docker.com/engine/reference/commandline/images/], one image for php + nginx and one for mysql.   

Setup Nginx + PHP service

Create the file  docker-compose.yml inside your application with this:    version: "3.1" services:   php-fpm:     image: webdevops/php-nginx:7.4     container_name: myapp-webserver     working_dir: /app     volumes:       - ./:/app     environment:       - WEB_DOCUMENT_ROOT=/app/webroot     ports:       - "80:80"   Now,we have a service named php-fpm, which is able to run php 7.4 and nginx at port 80 pointing to our webroot dir. Important note: the container_name must be unique in your system.   

Setup MySql service

Our MySql service requires a username, password and database name. For this, we are going to create the file mysql.env (don’t use a weak password in production, you could share a mysql.env.default file with your team) with this content:   MYSQL_ROOT_PASSWORD=password MYSQL_DATABASE=my_app MYSQL_USER=my_user MYSQL_PASSWORD=password   Now, at the end of docker-compose.yml , add this:      mysql:     image: mysql:5.6     container_name: myapp-mysql     working_dir: /app     volumes:       - .:/app       - ./tmp/data/mysql_db:/var/lib/mysql     env_file:       - mysql.env     command: mysqld --character-set-server=utf8 --init-connect='SET NAMES UTF8;'     ports:       - "3306:3306"   Before we start this service, lets add the service for our database, include this at the end of the file:  docker-compose.yml . You’ll see that we have - ./tmp/data/mysql_db:/var/lib/mysql, this allows us to persist mysql data. Now we also have a service named mysql with one empty database named my_app and a user name my_user.
 

Starting the services and app configuration

Before we continue, make sure that you don’t have any other http server or mysql server running. Now that we have finished our docker-compose.yml  we can execute docker-compose up to start the services and access the app at http://localhost. The next thing you need to do is update your database configuration with the correct credentials - the host is the service name, in our case it is “mysql”:   'host' => ‘mysql’,             'username' => 'my_user',             'password' => ‘password’,             'database' => 'my_app',   That’s it! Now we have a working local environment for our CakePHP app. We can now access the services using docker-compose exec php-fpm bash  and docker-compose exec mysql bash.  The files mentioned here (docker-compose.yml and mysql.env) can be found at  https://gist.github.com/CakeDCTeam/263a65336a85baab2667e08c907bfff6.  

The icing on the cake

Going one step further, we could add some alias (with linux) to make it even easier. Let’s add these lines at the end of your ~/.bashrc file:   alias cake="docker-compose exec -u $(id -u ${USER}):$(id -g ${USER}) php-fpm bin/cake" alias fpm="docker-compose exec -u $(id -u ${USER}):$(id -g ${USER}) php-fpm" alias composer="docker-compose exec -u $(id -u ${USER}):$(id -g ${USER}) php-fpm composer"   With those entries, instead of typing docker-compose exec php-fpm bin/cake, we can just type cake. The other two aliases are for composer and bash. Notice that we have ${USER}? This will ensure that we are using the same user inside the services.  

Additional information

Normally docker images allow us to customize the service, for webdevops/php-nginx:7.4 - you can check more information at: https://dockerfile.readthedocs.io/en/latest/content/DockerImages/dockerfiles/php-nginx.html and for mysql check: https://hub.docker.com/_/mysql . You can find more images at: https://hub.docker.com/. If you are not familiar with docker, take a look at: https://docs.docker.com/get-started/overview/, as this documentation provides good information.   Hope you have enjoyed this article and will take advantage of docker while working in your CakePHP application.  

Debug & Run Gitlab Pipelines In Your L...

Some time ago, we established https://git.cakedc.com/ as our company workflow. Along with it we created automated tools to support a continuous integration environment, with automated deployments based on develop, qa, stage, master branches and some useful tools to run on specific branches. We used jenkins for a long time, then switched to gitlab around version 6 (more than 5 years ago!) and we've been using it since. Gitlab provides a very powerful way to configure your pipelines and define specific docker images to be used as your runners. So we defined our own runner image and configured it to provide the typical dependencies needed to run static analysis tools, unit tests and other utilities as part of our build process. For example, one typical build file for a simple CakePHP project could be: # https://hub.docker.com/r/jorgegonzalezcakedc/cakephp-runner image: jorgegonzalezcakedc/cakephp-runner:yarn   before_script:   # install ssh-agent   - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'     # run ssh-agent   - eval $(ssh-agent -s)     # add ssh key stored in SSH_PRIVATE_KEY variable to the agent store   - ssh-add <(echo "$SSH_CI_PRIVATE_KEY")   - echo "$SSH_CI_PRIVATE_KEY" > /root/.ssh/id_rsa   - chmod 600 /root/.ssh/id_rsa     # replace git oauth key for composer   - sed -i "s/__TOKEN__/$GIT_OAUTH_TOKEN/g" ~/.composer/auth.json   variables:   # Configure mysql service (https://hub.docker.com/_/mysql/)   MYSQL_DATABASE: app_test   MYSQL_ROOT_PASSWORD: root   stages:   - test   - deploy   test:mysql:   services:   - mysql:5.7.22   script:   - echo $MYSQL_PORT_3306_TCP_ADDR   - composer install --verbose --prefer-dist --optimize-autoloader --no-progress --no-interaction   - ( [ -f vendor/bin/phpunit ] && echo "phpunit already installed";) || composer require phpunit/phpunit   - mysql -uroot -p$MYSQL_ROOT_PASSWORD -h $MYSQL_PORT_3306_TCP_ADDR -e 'CREATE DATABASE test_myapp_template;';   - DATABASE_TEST_TEMPLATE_URL="mysql://root:$MYSQL_ROOT_PASSWORD@$MYSQL_PORT_3306_TCP_ADDR/test_myapp_template" bin/cake db_test -i   - DATABASE_TEST_URL="mysql://root:$MYSQL_ROOT_PASSWORD@$MYSQL_PORT_3306_TCP_ADDR/app_test" DATABASE_TEST_TEMPLATE_URL="mysql://root:$MYSQL_ROOT_PASSWORD@$MYSQL_PORT_3306_TCP_ADDR/test_myapp_template" QUEUE_DEFAULT_URL='null:///?queue=default&timeout=1' vendor/bin/phpunit --verbose --colors=never   except:   - tags   deploy_develop:   stage: deploy   environment:     name: develop     url: https://www.cakedc.com   script:     - cd deploy && php deployer.phar deploy develop -vvv   only:     - develop   except:     - tags   In this case, on every push to the "develop" branch, we'll run unit tests of the project, then call the specific deploy script to push the project to our CI environment. This process is usually smooth and clean,  if it's not,  then you need to debug why the runner is failing at some step.   One possible answer to this situation would be to dockerize the project and ensure the local docker version matches 100% the runner being used, so you don't have surprises while running your pipelines.  This process is actually done in some projects to ensure we match specific versions and dependencies. But for legacy projects, it's useful to have something more or less generic that just works™ and does not require the effort to dockerize. In this case, and going back to the topic of the article, how can we debug the issues locally without waiting for the pipelines to run? (Note I'm using Ubuntu 16.04 as my dev environment, and possibly aiming to switch to 20.04 LTS soon…)

  • Install docker in your local machine see https://docs.docker.com/get-docker/ 
  • Ensure docker is up and running sudo service docker start
  • Install the gitlab apt repositories curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
  • Install the gitlab-runner package sudo apt update && sudo apt install -y gitlab-runner
  • Go to your local project, where the .gitlab-ci.yml file is located
  • Run your pipeline locally, note you can pass environment variables via --env and you can name the target you want to build, in this case test:mysql gitlab-runner exec docker test:mysql --env SSH_CI_PRIVATE_KEY="`cat ~/.ssh/id_rsa`" --env GIT_OAUTH_TOKEN="XXX"
  • If there's a problem with the pipeline, add a long sleep time in your .gitlab-ci.yml file to keep the pipeline up and running while you connect to it, for example after the like to run your unit tests, add a new line sleep 1337
  • Run your pipeline again, you'll notice it won't stop…
  • Open a new terminal and check the id of the docker instance using docker ps
  • You'll see a list of the active docker container IDs
  • Finally connect to the container using docker exec -it CONTAINER_ID bash
  • If bash is not available in the container, you'll need another way to connect to it (or another container)
  Once you get access to the container, you can manually execute commands, check permissions, run shells, and debug db and code contents to hunt down the bug you should have already in a unit test… This method saved me some time trying to understand a specific issue in a pipeline, I hope it'll save some of your time too!  

CakePHP 4 - First Look

Last december, the CakePHP team announced the immediate availability of 4.0.0. This release begins a new chapter for CakePHP, as 4.0 is now API stable. With this release, Cake 3.x moves into maintenance mode, while 2.x moves into security release mode. The promise of the version is: cleaner, faster and still tasty as usual. I had the opportunity to bake a new application from scratch and I will give my feedback about my process.  

Skeleton Design

The new version refreshes the skeleton design of the application. Now we have 2 new folders on root:
  • Templates

The templates folder has presentational files placed here: elements, error pages, layouts, and view template files. Pay attention for subfolders: 
  • Core templates are lowercase: cell, element, email, layout
  • App templates still uppercase: Error, Pages
  • Resources

The resources folder has subfolders for various types of resource files.  The locales* sub folder stores string files for internationalization.   If you are familiar with i18n, you will see the difference:
  • src/Locale/pt_BR/default.po (3.x)
  • resources/locales/pt_BR/default.po (4.x)
  Another important change was the .ctp files. They are moved for .php. CakePHP template files have a default extension of .php now. We have a new config/app_local.php file, which contains the configuration data that varies between environments and should be managed by configuration management, or your deployment tooling.  

PHP Strict Type Mode

In PHP the declare (strict_types = 1); directive enables strict mode. In strict mode, only a variable of exact type of the “type declaration” will be accepted, or a TypeError will be thrown. The only exception to this rule is that an integer may be given to a function expecting a float. This is a feature from PHP 7 - which we strongly recommended. All codebase from the skeleton and files generated by bake will include the function.  

Entities

The preferred way of getting new entities is using the newEmptyEntity() method: $product = $this->Products->newEmptyEntity();  

Authentication

After 10 years baking, that's a really big change for me. I don't usually use plugins for authentication, I really like the Auth Component. I think many bakers would agree, as I remember on the first international meetup, the co-host shared the same opinion.   The Auth Component is deprecated, so it's better move on and save the good memories. The new way for implementing Authentication is more verbose. It requires a few steps, I won't go into detail about that,  because you can easily check on book:
  • Install Authentication Plugin
  • Load the Plugin
  • Apply the Middleware
  • Load the Component
  My first look is like I said,  too verbose, for me anyway. We need to write a lot of code. Also it is not included on the skeleton of CakePHP applications, you need include by your own. https://book.cakephp.org/authentication/2/en/index.html  

HTTPS Enforcer Middleware

Contrary to the Authentication, I was really surprised how easy it was to force my Application to use HTTPS. If you are familiar with CakePHP, you will use the Security Component for that: class AppController extends Controller {      public function initialize()    {        parent::initialize();        $this->loadComponent('Security', [            'blackHoleCallback' => 'forceSSL',        ]);    }      public function beforeFilter(Event $event)    {        if (!Configure::read('debug')) {            $this->Security->requireSecure();        }    }      public function forceSSL()    {        return $this->redirect(            'https://' .            env('SERVER_NAME') .            Router::url($this->request->getRequestTarget())        );    }   }
  The implementation on version 4 is less verbose and easy, kudos for the new version:    public function middleware(MiddlewareQueue $middlewareQueue)    {        $middlewareQueue            ->add(new HttpsEnforcerMiddleware([                'redirect' => true,                'statusCode' => 302,                'disableOnDebug' => true,            ]));          return $middlewareQueue;    }   What I know is a drop, what I don’t know is an ocean. The new version is here to stay, and this article it's a just one overview of basic usage of the new version. * Version 4.1.0 is released already with more improvements and features.  

Links 

[1] Book https://book.cakephp.org/4/en/contents.html [2] Migration Guide https://book.cakephp.org/4/en/appendices/migration-guides.html  

CakeDC API plugin - Authentication and...

This article covers new changes for CakePHP 4 version of plugin. So it covers versions starting from 8.x (8.0) and later.  

Permissions system. RBAC

By default, the plugin uses CakeDC Users and CakeDC Auth plugins for authentication. For RBAC it uses the same style as defined in the Auth plugin RBAC system with minor changes required for the API plugin. First, let's consider the case when we want public api without any authorization. In this case the most simple way would be is to define in config/api_permissions.php next rule   return [     'CakeDC/Auth.api_permissions' => [         [             'role' => '*',             'service' => '*',             'action' => '*',             'method' => '*',             'bypassAuth' => true,         ],      ], ];   Now, consider the case we want to use users plugin authentication. Since Api is supposed to be used from another domain, we should allow all requests with OPTIONS type. To do this we should add this rule as first on in config/api_permissions.php       [         'role' => '*',         'service' => '*',         'action' => '*',         'method' => 'OPTIONS',         'bypassAuth' => true,     ],    Here, method define OPTIONS and bypassAuth means that such actions should work for any users, including not authenticated. Now we should allow Auth service methods       [         'role' => '*',         'service' => '*',         'action' => ['login', 'jwt_login', 'register', 'jwt_refresh',],         'method' => ['POST'],         'bypassAuth' => true,     ],    All other services/actions should be declared in api_permissions file to define what user roles are allowed to access them. Imagine we want to allow the admin role to access the add/edit/delete posts and make index and view public. We can do it based on method or based on action names.       [         'role' => 'admin',         'service' => 'posts',         'action' => '*',         'method' => ['POST', 'PUT', 'DELETE'],     ],      [         'role' => 'admin',         'service' => 'posts',         'action' => ['index', 'view'],         'method' => '*',         'bypassAuth' => true,     ],   

 Routers and Middlewares

Starting from the 8.x version, API Plugin uses router middlewares. This gives great abilities to configure the plugin. So now it is possible to have separate authentication and authorization configuration for website and for api. Also, It is possible to have more then one api prefix, and as result provide more then single api for website with different configuration. Let’s take a look on the default configuration for middlewares   'Middleware' => [     'authentication' => [         'class' => AuthenticationMiddleware::class,         'request' => ApiInitializer::class,         'method' => 'getAuthenticationService',     ],     'bodyParser' => [         'class' => BodyParserMiddleware::class,     ],     'apiParser' => [         'class' => ParseApiRequestMiddleware::class,     ],     'apiAuthorize' => [         'class' => AuthorizationMiddleware::class,         'request' => ApiInitializer::class,         'params' => [             'unauthorizedHandler' => 'CakeDC/Api.ApiException',         ],     ],     'apiAuthorizeRequest' => [         'class' => RequestAuthorizationMiddleware::class,     ],     'apiProcessor' => [         'class' => ProcessApiRequestMiddleware::class,     ], ],   First we see the order of middlewares that proceed api request. It passes through AuthenticationMiddleware, AuthorizationMiddleware, and RequestAuthorizationMiddleware to perform generic auth tasks. It passes through BodyParserMiddleware to unpack the json request. And finally ParseApiRequestMiddleware does initial service analysis and ProcessApiRequestMiddleware performs the request. Also we can note CakeDC\Api\ApiInitializer class used to define Authentication and Authorization configuration. It can be redefined in the application layer to provide needed Identifiers and  Authenticators.  

 Jwt authentication - Refreshing tokens

New plugin feature is embedded jwt_login action which allows the user to get access_token and refresh_token included into the login response. Tokens should be passed in the Authorization header with bearer prefix. Access token is supposed to be used as default token and refresh token needed to get a new access token when it's expired. So for refreshing provided additional jwt_refresh action which should be used in this case.  

 Configuration

Configuration should be defined on application level in config/api.php. Need to note that it is important to enable this file to load by the Api plugin. It could be done in config/bootstrap_app.php using global configuration: Configure::write('Api.config', ['api']);       'Api' => [          ...                  'Jwt' => [             'enabled' => true,             'AccessToken' => [                 'lifetime' => 600,                 'secret' => 'accesssecret',             ],             'RefreshToken' => [                 'lifetime' => 2 * WEEK,                 'secret' => 'refreshsecret',             ],         ],    Hopefully, this was helpful. Our team is always working on adding new features and plugins. You can check out more available plugins HERE.

CakePHP Meetup: Unit Test Fixtures, Qu...

Developers are used to living in a virtual world, so adjusting has been easier than expected. Recently, we’ve been holding virtual meetups, and we are so happy with the feedback. Digital training sessions allow bakers from all over the world to come together and enjoy. Our plan is to host one each month, and coordinate time zones so that everyone gets a chance to attend. Our latest one was based around a good time for our Japanese community.  If you missed the meetup, no problem. We always post the recording for playback, and I’ll even give you a quick rundown of the topics covered. Let’s jump in:

CakePHP Fixture Factory Plugin

by Juan Pablo Ramirez CakePHP Fixture Factory Plugin https://github.com/pakacuda/cakephp-fixture-factories  helps to improve the way fixtures are generated, when having a big database writing fixtures can get so complicated. This plugin provides Fixture Factories in replacement of the fixtures found out of the box in CakePHP.
Generating fixtures can be done in a few code lines reducing the effort of writing and maintaining tests. There are some other plugins to manage fixtures: 

CakePHP Queue Plugin

By Mark Scherer @dereuromark CakePHP Queue Plugin https://github.com/dereuromark/cakephp-queue is a simple Queue solution, it can be used for small applications and it’s a good one to get started with Job Queues, having something easy to maintain at the beginning is a good starting point.
Queues are a good option for functionalities like: image processing, email sending, PDF generation; to improve the response-time for heavy-processing tasks. For more robust solutions can be used:
  • CakePHP Queuesadilla  https://github.com/josegonzalez/cakephp-queuesadilla This plugin is a simple wrapper around the Queuesadilla queuing library, providing tighter integration with the CakePHP framework. We have used this plugin in CakeDC in several projects, we also had to build  a Mongo Engine for a specific client.

CakePHP PHP PM Bridge

By Jorge Gonzalez @steinkel CakePHP Bridge https://github.com/CakeDC/cakephp-phppm  to use with PHP-PM project.
PPM is a process manager, supercharger and load balancer for modern PHP applications. PHP PM It's based on ReactPHP, the approach of this is to kill the expensive bootstrap of PHP (declaring symbols, loading/parsing files) and the bootstrap of feature-rich frameworks.
It’s a good option If you want to significantly improve the responsiveness of an application that could have spikes. PM works as PHP FPM, it’s a replacement for it.  Below some benchmark:  50 Concurrent threads in 10 seconds
  • FPM 83 transactions per second, Failed 0,  Concurrency 6.58.
  • PPM 90.30 transactions per second, Failed 0, Concurrency 3.86.
200 Concurrent threads in 10 seconds
  • FPM 116,49 transactions per second, Failed 142,  Concurrency 116.64.
  • PPM 207.35 transactions per second, Failed 0, Concurrency 85.59.
1000 Concurrent threads in 10 seconds
  • FPM 109,88 transactions per second, Failed 1759, Concurrency 187.49.
  • PPM 214.91 transactions per second, Failed 0,  Concurrency 302.39.
PPM is able to handle a lot of concurrency connections coming in spike to the server  in a better way than PHP FPM.
For watching the Meetup visit the following link https://www.youtube.com/watch?v=POI0IwyqULo Stay up to date on all virtual meetups here  https://cakephp.org/pages/meetups      

Working From Home - A Developer's View

Long before COVID-19, I (and my team) started working from home. Even before working at CakeDC, between 2007 and 2010, I worked for a company where you were able to split your time between home and office.    Maybe that's why I actually feel comfortable working remotely and I would never change it for an in office position anymore.     Anyway, I am not going to talk about remote work because these days there are thousands of articles about it (even in  one of our previous posts). I want to focus today on writing about our experience with different communication tools and the pros and cons for each of them.   Back in 2011 when I started working at CakeDC, we had an IRC server in place with a couple of plugins to retain messages for our day-to-day communication. Everyone used different clients like Colloquy on mac or Pidgin on Linux. Additionally, we used Skype for peer/team calls and also for client communication. In my opinion, the linux experience was awful until they improved the client a few years later. This setup was implemented in 2007 when the company was started, and in 2012, we decided to shut it down because it was easier just using Skype for everything, messages and calls.     After several years using - suffering - Skype, with new options in the market, we decided to move away to a more reliable and modern approach. The main reason was the lack of updates for Skype linux client, and the message retention limits. In 2016 we started utilizing the more than popular Slack and its open source alternative, Rocket Chat; always keeping Skype for client communication.    Some months later the team concluded that Rocket Chat was the right choice, mainly because we wanted to have control over the messages and the information transmitted. Since the features we were using were available in both solutions, we installed our own Rocket Chat server. At this point I have to say that we did try the calls solution (Jitsy) inside Rocket Chat, but the experience was not good at all, issues with missing calls, poor call quality, etc, made us keep Skype for calls.   On the other hand CakePHP training was provided using Join.me and even when it worked very well in most situations; our trainer Jorge, always had to use a Windows machine instead of his usual Linux one. And then, Zoom emerged.     The year was 2018, when Zoom became so popular even though it started back in 2011 (yes, Zoom existed before COVID-19 crisis). We started using it and it quickly replaced Skype for our team calls. It allowed multiple people calls, screen share, etc. I must say, however, the Zoom chat is one of the worst things I have ever seen.   Going back to Jorge, as you can imagine he was very happy when he saw Zoom had a good Linux client. Unfortunately, he was quickly disappointed because the client screen crashed randomly, went black, and the computer ran out of memory when he tried to share his screen to more than 10 attendees. Happily he didn't throw his windows machine out of the window yet so he could continue giving the training with that machine.   It's 2020, COVID-19 is around us, and Zoom is probably the most popular telecommuting tool. For both adults and children, it is an essential tool to keep working and studying. However, fame never comes alone and threats, rumors and comments are making us move away (again) from Zoom to Google Meet. Also, it didn't make sense to pay for Zoom if we were already paying for GSuite.    I didn't mention it before, but we have been using GSuite (former Google Apps) since the beginning. Google - late as usual - detected a market niche and decided to pump out its meeting tool. In my opinion, I am a standalone app person. This means that I will always prefer having 20 apps instead of 20 browser tabs. So, I don't like Google Meet a lot because of this, but I must say the call quality is superb.    I am not sure how or when we will move to other tools, but right now we are very happy with our Rocket Chat installation and we are still getting used to Google Meet, but it fits our needs.    As a side note we are still using skype to communicate with clients because it is the app everyone has, and sometimes people simply don't want to install something else or use something they are not used to.     To conclude I must say that each team and person should try all of these different tools before choosing one, because one tool that may fits my needs may not fit yours.     

Links

Rocket Chat - https://rocket.chat/ Google Meet - https://meet.google.com Zoom - https://www.zoom.us Skype - https://www.skype.com Colloquy - https://colloquy.app/ Pidgin - https://pidgin.im/

CakePHP Virtual Meetup

Unless you’ve been on Mars for the past few months, you know that things have changed. We can no longer prance worry-free into a department store, or go grab drinks with friends. Everything is now masks, grocery delivery, and video chats. While this was a hard transition at first, we have surprisingly become well-adjusted.  

Virutal Meetups

Our past 2 CakePHP meetups have been in the form of virtual meetups, and it has been a huge success. Normally, meetups are restricted to a certain city, state or even continent in the world, but not anymore! We are over here like, “why haven’t we done this before?”. The idea that CakePHP lovers from all over the globe came come together, live, at the same moment and bake together… it’s amazing.    The first virtual meetup, was suggested and hosted by Mark Scherer (Dereuromark on Twitter). He came to the CakeDC team and asked about joining forces and using one platform to bring everyone together. While picking a meetup time is difficult, given the differences in zones, we tried to find a happy place in the middle of them all. The community team will let this vary from time to time for those, say in Japan, who still found it difficult to tune in. Luckily, even if the time isn’t ideal for you, we ALWAYS record and post the session for playback.    If you’ve missed these (you can still catch the playback!), and want to know more, it's a simple setup: the organizer can give access to any speakers (panelists), they present their talks/trainings and everyone gets to tune in from the comfort of their couch. No worries about background noise, as only speaker's mics are enabled. There is a Q&A feature that allows attendees to ask/post their questions in real time.  For the first meetup, the main topic was a CakePHP 4.1 preview, and we can spend all day on all of the new stuff in CakePHP 4. Also covered was: IdeHelper, Auth with Authentication plugin and fixture factories. We didn’t really know what to expect, or what would go wrong, but things went great! We had a lot of good feedback, which of course inspired everyone to do it again! We got Jorge on board (steinkelz on Twitter) and even tried out a new platform, RingCentral (shout out, thanks guys!). This meetup explored Baking a new plugin and TDD for it’, Authorization Plugin, Pro-Debug with Xdebug + stepping through in IDE, and Mark Story checked in to talk about common-table-expressions  

Missed the meetups?

PLAYBACK OPTIONS: April Meetup: LISTEN HERE  May Meetup: LISTEN HERE    My favorite part about these virtual meetups is that bakers from all over can get involved, and we get to hear from some people that without the digital world, wouldn’t be possible. The CakePHP community team is always looking for speakers for the next meetup, so if you’re reading this thinking, “i’d really like to talk about ___!”, then we want to hear from you! You can also submit for a lightning talk - the email is community@cakephp.org. We are just so thankful that we get to bring so many Cake lovers together with one common goal in mind, to bake and learn new recipes.   

There will be more!

So, how will you find out about future meetups? Easy! Follow CakePHP (or CakeDC) on social media: Twitter / Facebook. You can also sign up for the CakePHP Newsletter (and stay informed about other fresh news, too!). Training sessions can be found at training.cakephp.org.    Have an idea for the next topic? What do YOU want the speakers to talk about? Let us know. Our next session is scheduled for June 18. See details HERE.

PHP Quality Assurance Tools with CakePHP

In Software Development, we are always looking for resources to improve the products we deliver. In this article we show some awesome php qa tools to help us improve our CakePHP software quality and development process. Let's dive in. Note: The snippets provided in this article can be found at https://github.com/CakeDC/phpqa-app-example
 

Coding Standards

Choosing a coding standard for your project will help the developers team speak the same language by defining a set of rules on how to name variables, methods, class and etc. The coding standard usage can make the integration of community libs and plugins easier. Checking and fixing coding standards are easily done with the awesome tool PHP_CodeSniffer, this tool has two main scripts:
  • phpcs check for coding standard violations
  • phpcbf autofix coding standard violations, keep in mind that not all errors will be autofixed, but will help you a lot.
     
CakePHP offers a customized coding standards at https://github.com/cakephp/cakephp-codesniffer and you should be using it.
  Let’s install the tool and the coding standard: composer require --dev cakephp/cakephp-codesniffer:~4.1.0
Create the file phpcs.xml <?xml version="1.0"?> <ruleset name="App">    <config name="installed_paths" value="../../cakephp/cakephp-codesniffer"/>    <rule ref="CakePHP"/> </ruleset>
Update “scripts” block in composer.json  with: "scripts": {         "cs-check": "phpcs -p ./src ./tests",         "cs-fix": "phpcbf -p ./src ./tests",
  Now we can run composer cs-check and composer cs-fix. In a sample app I got this output saying some errors can be autofixed with composer cs-fix


Static Analysis

How many times have you deployed a project on a production server and found a silly error like calling an undefined method? I hope not often. To avoid this type of issue, a static analysis tool is useful. I recommend you to use PHPStan and PSalm, these tools will help you find errors without having to run the application.

PHStan

PHPStan will rely on phpdoc, so that’s one more reason to use a coding standard tool like PHP_CodeSniffer. Also I recommend that you use the plugin: cakephp-ide-helper, to update annotations (phpdoc) in your app classes. In some cases we need to help PHPStan understand our code better, and for this reason we created a custom extension: https://github.com/cakedc/cakephp-phpstan. Let’s install PHPStan using composer: composer require --dev phpstan/phpstan phpstan/extension-installer cakedc/cakephp-phpstan:^1.0
  We also need to create two config files Includes:     - vendor/cakedc/cakephp-phpstan/extension.neon     - phpstan-baseline.neon parameters:     level: 6     checkMissingIterableValueType: false     checkGenericClassInNonGenericObjectType: false     autoload_files:         - tests/bootstrap.php     ignoreErrors: services: parameters:     ignoreErrors:
  And add two scripts into “scripts” block from composer.json "stan": "phpstan analyse src/",         "stan-rebuild-baseline": "phpstan analyse --configuration phpstan.neon --error-format baselineNeon src/ > phpstan-baseline.neon",
Now we can run composer stan and  composer stan-rebuild-baseline the second one will populate phpstan-baseline.neon to ignore all errors returned in composer stan so only use when all the errors shown should be ignored.  

Psalm 

Psalm is another awesome static analysis tool, it provides a way to check for errors in your code, and can fix some of them, too. In my experience psalm and phpstan work perfect, so you don’t need to pick one or the other, you can use both. Let’s install psalm: composer require --dev  "psalm/phar:~3.11.2"
We also need to create two config files <?xml version="1.0"?> <psalm     allowCoercionFromStringToClassConst="true"     allowStringToStandInForClass="true"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns="https://getpsalm.org/schema/config"     xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"     errorBaseline="psalm-baseline.xml" >
   <projectFiles>         <directory name="src" />     </projectFiles>     <issueHandlers>         <LessSpecificReturnType errorLevel="info" />         <RedundantConditionGivenDocblockType errorLevel="info" />         <TypeCoercion errorLevel="info" />         <DocblockTypeContradiction errorLevel="info" />     </issueHandlers> </psalm>
  <?xml version="1.0" encoding="UTF-8"?> <files psalm-version="3.11.2@d470903722cfcbc1cd04744c5491d3e6d13ec3d9"> </files>
  And add two scripts into “scripts” block from composer.json "psalm": "psalm.phar --show-info=false src/ ",         "psalm-rebuild-baseline": "psalm.phar --show-info=false  --set-baseline=psalm-baseline.xml  src/"

Now we can run composer psalm and  composer psalm-rebuild-baseline the second one will populate psalm-baseline.xml to ignore all errors returned in composer stan, so only use this when all the errors shown should be ignored. When we run composer psalm it may inform you that some errors can be autofixed and which arguments you should use to fix. Normally it will be something like vendor/bin/psalm.phar --alter --issues=MissingReturnType,MissingClosureReturnType --dry-run
Psalm will only change the file If you remove the --dry-run part.  

Testing

Testing is extremely important for any application development. There are many types of software for testing, in this article we are focusing on unit tests. Unit tests help us check if one specific piece of code is working as expected, you can think like ‘Is method ‘A’ returning the correct value when argument has value ‘Z’?’. In CakePHP we have built-in support to PHPUnit, CakePHP integration offers additional features to make it easier to run units tests like custom asserts and methods for integration tests, and fixtures logic for models. We can bake unit tests using the bake plugin. To improve your unit tests try the cakedc/cakephp-db-test with this one you can use database fixtures instead of files. Learn more at: https://www.cakedc.com/rafael_queiroz/2020/04/27/unit-testing-with-cakedc-db-test PHPUnit probably already is installed in your app but if not I recommend you to compare your project files with: https://github.com/cakephp/app. For more information check out: https://book.cakephp.org/4/en/development/testing.html   Hope you enjoyed this article and will start using these awesome tools to make your projects better than ever. Good Baking.  

Working with CakePHP Authorization

As you may know, there are 2 new plugins "recently" (not so recently) added to deal with the concepts of Authentication and Authorization in your CakePHP applications. Over the years, both Authentication and Authorization were managed in the Controller layer, via AuthComponent.  These 2 things usually grow in complexity as your project grows too, making the AuthComponent a complex class dealing with many features at the same time.
One of the original ideas behind these new plugins was to refactor AuthComponent and create specific layers to handle:

  • Authentication: who are you?
  • Authorization: are you allowed?
We are going to explore the Authorization concepts in this article using a specific example: Let's imagine we have some kind of game application where Users are going to manage Tournaments. The Users will be able to create new Tournaments, and join the Tournaments through a TournamentMemberships many to many association. Other users won't have access to the Tournaments unless they are invited to play. Players of a Tournament can invite other Users to play. So, a quick list of the use cases we are going to cover below are:
  • /tournaments/add  any user can create a new Tournament
  • /tournaments/index  browse all joined tournaments
  • /tournaments/invite  only current Members can invite others, and only if the Tournament has not started yet
We are assuming Authorization step is done in our application and we have a logged in user available in our request. At this point we'll also assume you've installed cakephp/authentication and cakephp/authorization and loaded both plugins. Authorization does not impose restrictions  on when the authorization checks will be done, let's quickly examine the workflow and related classes for Authorization:
  • AuthorizationMiddleware is attached to your Application, and will ensure the Authorization will be checked somewhere while processing the request.
     The unauthorizedHandler config will allow you to define what to do if the request was not authorized for some reason.
  • At some point in your code, you'll need to call AuthorizationComponent, either to
    • skipAuthorization when you don't require any specific condition to authorize the operation. Example: // ... somewhere in your beforeFilter...     if ($user->is_superadmin) {         $this->Authentication->skipAuthorization();     } // ...
    • authorize($resource, $action) when you need to check if a given user is allowed to do some action on a given resource. Note the resource must be an Object.


How Authorization checks are done?

  1. We start by checking the resource, it's an Object so we use a Resolver to map every resource with a given Policy. There are some common defaults, for example to map ORM classes.
  2. Once we get to a Policy class, we check the matching method, for example if the action is "invite" we would check the method canInvite(IdentityInterface $user, Tournament $tournament)
Configuration: After the Authentication middleware, in your src/Application.php class, add the Authorization Middleware            $authorizationService = new AuthorizationService(new OrmResolver());             ...             ->add(new AuthorizationMiddleware($authorizationService, [                 'unauthorizedHandler' => [                     'className' => 'Authorization.Redirect',                     'url' => '/users/login',                     'queryParam' => 'redirectUrl',                 ],             ]));   Note the $authorizationService is configured with one resolver to match the CakePHP typical ORM classes, like Entities or Queries. https://book.cakephp.org/authorization/2/en/policy-resolvers.html#using-ormresolver   Once the middleware is added, you'll need to ensure the Authorization is checked, or you'll get an  error?: "The request to / did not apply any authorization checks" . The first step would be to skip authorization for all the controllers and actions, for example in beforeFilter callback that all Users are allowed to access. About the previous Tournaments specific cases, we'll need to create a new Policy class including all the possible actions to be done, for example:
 
  • /tournaments/add
We need to create a new Policy for the Tournament Entity file src/Policy/TournamentPolicy.php to define policies related to specific tournaments class TournamentPolicy {     public function canAdd(IdentityInterface $user, Tournament $tournament)     {         // all users can create tournaments         return true;     } } file src/Controller/TournamentsController.php // ...     public function add()     {         $tournament = $this->Tournaments->newEmptyEntity();         $this->Authorization->authorize($tournament);         if ($this->request->is('post')) { // ... The call to $this->Authorization->authorize($tournament); will map the Tournament entity to the TournamentPolicy, by default the action is taken from the controller action, in this case "add" so we will need to define a canAdd() method. We allowed all Users to create Tournaments.  
  • /tournaments/index
We'll need to create a new policy for the TournamentsTable, and additionally a scope method to filter the Tournaments based on the current User membership. file src/Policy/TournamentsTablePolicy.php to define policies for the TournamentsTable class TournamentsTablePolicy {     public function canIndex(IdentityInterface $user, Query $query)     {         // all users can browse tournaments         return true;     }     public function scopeIndex(IdentityInterface $user, Query $query)     {         // scope to filter tournaments for a logged in user         return $query->matching('TournamentMemberships', function (Query $q) use ($user) {             return $q->where(['TournamentMemberships.user_id' => $user->get('id')]);         });     } } file src/Controller/TournamentsController.php     public function index()     {         $query = $this->Tournaments->find();         $this->Authorization->authorize($query);         $tournaments = $this->paginate($this->Authorization->applyScope($query));           $this->set(compact('tournaments'));     }
 
  • /tournaments/invite
file src/Policy/TournamentPolicy.php to define policies related to specific tournaments // ...     public function canInvite(IdentityInterface $user, Tournament $tournament)     {         return TableRegistry::getTableLocator()->get('TournamentMemberships')             ->exists([                 'user_id' => $user->get('id'),                 'tournament_id' => $tournament->get('id'),             ]);     } // ... file src/Controller/TournamentsController.php // ...     public function invite($tournamentId, $userId)     {         $tournament = $this->Tournaments->get($tournamentId);         $this->Authorization->authorize($tournament); // ...   In this case, we need to check if the logged in User is already a member of the TournamentMemberships group, if so, we are allowed to invite another user. As you can see, Authorization plugin will provide a flexible way to manage your application permissions.   In the previous examples we've covered typical application use cases to handle permissions per resource and action. New classes and interfaces, like policies, resolvers and mappers will allow you to configure the Authorization and ensure all the resources in your application will provide the required permissions. If you're looking for RBAC based on your controller actions, take a look at https://github.com/CakeDC/auth/blob/master/Docs/Documentation/Authorization.md For additional tools and plugins, check https://github.com/FriendsOfCake/awesome-cakephp#authentication-and-authorization  

We Bake with CakePHP