Using bake without losing custom methods?


#1

I’m currently developing my own CMS (for private use) as some may have noticed and for this, I update my database and models quite a bit (new features require new tables, columns etc.).
Currently, I keep rebuilding my models using cake bake model all.
This, however, comes with one downside: it completely overwrites my custom methods.
For example, in my Model/Table/UsersTable.php I have the following method:

/**
 * Add the role for a user after it has been saved
 * https://discourse.cakephp.org/t/insert-with-related-rable/4384
 */
public function afterSave(Event $event, EntityInterface $entity, \ArrayObject $options) {
  if($entity->isNew()) {
	    $usersRole = $this->UsersRoles->newEntity();
	    $usersRole->user_id = $entity->id;
	    $usersRole->role_id = 1; // TODO: allow changing this value through a setting while creating a user using the admin dashboard(using $options?).
	    $this->UsersRoles->save($usersRole);
    }
}

However, every time I re-build the model using bake, this method gets removed (along with some other custom methods like this).
So my question is, how do I keep this from happening?
Currently when I use bake, I then go to my Git client (Gitkraken in my case) and discard the changes that removes my methods.
Far from ideal, so that’s why I decided to bring this up.


#2

Hello FinlayDaG33k,

Short answer: it seems you can’t

Long answer: Looking into the source-code of the BakeShell (using v3.3, on my current project) I don’t see any flag or something that indicates any possibility to overwrite only parts of the respective files - and from my point of view, this would be out of scope for bake (as I understand bake as a smart tool to generate a basic skeleton of working code, which you can then customize to fit your needs).

Make yourself familiar with defining ER on table-level and internally used naming conventions - because this is where cacephp can save you time in long term.
See: https://book.cakephp.org/3.0/en/intro/conventions.html for more information on conventions

BR


#3

Hm…
I see…
I just don’t feel very comfortable at the moment with manually creating models and such.

Would be nice to see a way to keep custom methods like these.
I have looked into stuff like behaviours and eventlisteners, but it seems like they are on an app-level instead of table-level (correct me if I’m wrong)


#4

Well, you don’t need to completely create them manually - just extend/change associations you add later (Sorry if I misunderstand you here).

Behaviors basically work on table-class-level (you attach/configure them in your table class), but are rather used to provide DRY code in your models - as you can encapsulate common table-class-level code in there.
The Event-System crosses all layers of the app (you are already using it with your afterSave-function), but also does not help you to automagically update only specific parts of your code…

The problem here is: How could software like “bake” detect, which parts should be updated and which shouldn’t?


#5

How could software like “bake” detect, which parts should be updated and which shouldn’t?

I don’t really know, well, maybe detect some stuff based on conventions?
I don’t know if afterSave() can be generated automagically anywhere in bake?
If not, I think CakePHP could be able to load/overwrite methods in some other file.
like for example Model/Extension/UsersTableExtension.php (please don’t hate if I made mistakes, it’s just a quick mockup):

<?php
  namespace App\Model\Table;

  // Just an example, the Devs should come up with the convention n such
  class UsersTableExtension extends UsersTable {
    public function afterSave(Event $event, EntityInterface $entity, \ArrayObject $options) {
      // do stuff
    }
  }

Then bake doesn’t have to do anything in the UsersTableExtension.php, so it doesn’t need to be overwritten.
afterSave() is obviously just one example this could be used for.
Basically, we take all the extending stuff (like afterSave(), beforeDelete(), beforeFind() etc.) and put it somewhere else.
CakePHP will then load up the UsersTable.php and see if there’s a UsersTableExtenstion.php.
If so, it will load and overwrite this stuff in UsersTable.php.
This way, bake can overwrite the model itself (like the initialize(), validationDefault() etc.), but won’t overwrite the other stuff like afterSave().
And if somebody wants to add something to let’s say the initialize() function, he/she could just do it the same way as normal, using the parent::initialize() method.

I don’t know how feasible this is as I haven’t dived that deep into CakePHP yet, but I think this could be done.

EDIT: I’ll actually take this idea to the git repo to start a discussion about this over there


#6

I think it’s a valid question but not a solution bake was made for in the first place; it was meant to dave time building standard code. It creates filledged files, damned if they already exist.
Let’s take a step back from your suggested solution to your problem; you want to ‘rebake’ your models (I hate to think what a real cake would taste like after rebaking, but that’s another topic), and you do not want to lose custom code.
I would try and work with helpers. They are additional code files with functions that can be called from the basic files. If you strictly separate the basic behaviour from the custom functions, that would save you a lot of work. I would just copy the ctp files elsewhere and place them back after baking.


#7

Btw: It is possible to “overwrite” the templates that are used when baking as well as define new commands (that do not overwrite your custom methods)

See: https://book.cakephp.org/3.0/en/bake/development.htm for more details…


#8

Using a custom template for bake or helpers, might be options, but I think it would save users more time and effort if Bake wouldn’t overwrite them in the first place.
Also, bake might not be designed for this in the first place, but it would be a nice way of extending bake.
It would make CakePHP a lot more “rapid” to use in the long run


#9

Hi,

I face this same issue daily - the way i work around is manual but quite quick and easy - i use PHPStorm.

  1. rebake your models, overwrite all files
  2. Select each of the 4 files that have changed, right click > local history > show history

you now have a nice diff view you can pull your custom changes back in.

Its either the above, or I manually update my model code after making changes.


#10

well, it’s doable when you only have 2 or 3 models… but imagine scaling this up to 20+ tables…
On the github repo, we were discussing this a bit and after a while, I settled with Traits.
Once I finished my current issue, I’ll try to update bake to keep the use statements (which basically load the traits).


#11

Totally agree that would be painful.

If memory serves, the Symfony2 (yes been a while) framework worked around this issue by having two classes for a table, one been a subclass of the table class. One class was your generated code - the other was where you put your custom logic… maybe something like that would work?


#12

That was what this thread was aimed at indeed :slight_smile:
However, I opened an issue on the CakePHP repo as well so I could discuss it over there.
After a while, we came to the consensus to use Traits in order to not re-invent the wheel with a custom way of doing this.
I’m currently trying to understand the current Bake code so I (or if somebody else feels up to it) can make bake to re-add use statements.
Once that is complete, one can use a trait to write the custom login, and then add a use to the main model to load the traits.
Bake should then (after patching it) no longer overwrite these use statements added by the user so it won’t remove the custom logic.