CakeDC Blog

TIPS, INSIGHTS AND THE LATEST FROM THE EXPERTS BEHIND CAKEPHP

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.

Latest articles

Using Postgres as default database- hints, tricks and tips

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 relations

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.   

We Bake with CakePHP