# |
May 19th 2021, 16:54 |
me1367 |
I never worked with CakePHP 2 so I have no clue how the ORM was for that :P |
# |
May 19th 2021, 16:54 |
khalil |
Then it's me |
# |
May 19th 2021, 16:53 |
me1367 |
dunno |
# |
May 19th 2021, 16:53 |
khalil |
Is it just me or the new ORM is pretty confusing? |
# |
May 19th 2021, 16:52 |
me1367 |
never gotten the time to do so xD |
# |
May 19th 2021, 16:52 |
me1367 |
My site is still not running CakePHP 4.x :P |
# |
May 19th 2021, 16:52 |
khalil |
(for your eyes only) I switch to Cake 4 months ago, after using Cake 2 for like 7 years :face_with_hand_over_mouth: |
# |
May 19th 2021, 16:50 |
me1367 |
I'm slowly upgrading it for CakePHP 4.x |
# |
May 19th 2021, 16:50 |
me1367 |
x) |
# |
May 19th 2021, 16:50 |
khalil |
PS: I'm checking admiral :) |
# |
May 19th 2021, 16:50 |
khalil |
Yuppp |
# |
May 19th 2021, 16:50 |
me1367 |
And that's where good old OOP got ya covered :) |
# |
May 19th 2021, 16:49 |
khalil |
That's basically why I wanted a way to have something I can access anywhere, instead of repeating the same logic in a component and a helper :P |
# |
May 19th 2021, 16:48 |
khalil |
Hahahaha Always :) |
# |
May 19th 2021, 16:48 |
me1367 |
also, keep your code DRY :P |
# |
May 19th 2021, 16:47 |
me1367 |
It's just basic OOP, generally, unless you really screw up, it's fine to do so :P Just make sure you keep to the _general_ *structure* of CakePHP and you'll be fine. |
# |
May 19th 2021, 16:46 |
khalil |
Very helpful! thanks again :)) |
# |
May 19th 2021, 16:46 |
khalil |
Perfect man I was struggling with this and I had no idea about the utility class, and I always worry about doing something that is considered against CakePHP principles |
# |
May 19th 2021, 16:45 |
me1367 |
Ah yes, I thought you wanted it the opposite way around (from USD to local currency, not local currency to USD). Got it. But yea, that's just in the `beforeSave` . You'd end up with something like this: ```public function beforeSave() { $converter = new CurrencyConverter(); $converted = $converter->convert($amount); }``` |
# |
May 19th 2021, 16:43 |
khalil |
That's why I need access there :) |
# |
May 19th 2021, 16:42 |
khalil |
Now in the view, I can have a simple helper that convert the product price (USD) to your currency (saved in cookie) - when the vendor wants to add a product, I convert the price they enter (in their current currency) to USD and save it in the db ( I should do that in a beforeSave hook on the model level) |
# |
May 19th 2021, 16:41 |
khalil |
@me1367 I think you got me wrong, each product, needs a price to be saved in the db in the end, the base price in the db is USD, the exchange happen in the views, and when adding / editing products, meaning, the price in the db is $100, when you open the site, I detect if you're logged in, if yes, I get your currency (saved in the user profile) if you're not logged in, I check if you have a cookie "currency" saved, if yes, then |
# |
May 19th 2021, 16:41 |
slackebot |
I use that, if not, I detect your ip from a local library, detect your location, and based on your location I know your currency, this only happens on first visit cause after that I save it in a cookie, |
# |
May 19th 2021, 16:39 |
neon1024 |
There surely must be an easier way right? |
# |
May 19th 2021, 16:38 |
khalil |
That's not how it's built, the exchange rates are in php file on the server that gets updated automatically every 24 hours, the exchange rates base currency is USD, so that part is pretty straight forward |
# |
May 19th 2021, 16:31 |
neon1024 |
Ooh, `withAttribute()` late afternoon blindness 8) |
# |
May 19th 2021, 16:30 |
neon1024 |
Or is another instance where I need to actually instantiate a bunch of stuff for the test? |
# |
May 19th 2021, 16:29 |
neon1024 |
How do I set an attribute on a ServerRequest instance? From what I can see in the code it’s literally a class property, but my unit test is telling me it’s getting a NULL and expecting an instance of AuthorizationService |
# |
May 19th 2021, 14:14 |
ndm |
no problem |
# |
May 19th 2021, 14:13 |
neon1024 |
Signs are good, thanks again @ndm really appreciate the help and guidance with all this new Cake 4 stuff :thumbsup: |
# |
May 19th 2021, 14:03 |
neon1024 |
I’ll try a middleware and see how I get on |
# |
May 19th 2021, 14:03 |
neon1024 |
Attaching the behaviour using Model.initialize inside the Application.php works great, but again, I have no session there |
# |
May 19th 2021, 14:02 |
ndm |
Well, that sounds all very theoretical to me right now, not sure what depends on what and when and.... :upside_down_face: |
# |
May 19th 2021, 14:02 |
neon1024 |
Then, post Authorize, four tables stopped being filtered, and you nailed it. They’re all part of the auth finder :) |
# |
May 19th 2021, 14:02 |
neon1024 |
Hence the global bind using Model.initialize :) |
# |
May 19th 2021, 14:01 |
neon1024 |
Then the behaviour just amends the queries |
# |
May 19th 2021, 14:01 |
neon1024 |
As if the user has the multi organisation permission, they can change to another organisation and I’ll update the session to track the data they can see |
# |
May 19th 2021, 14:00 |
neon1024 |
I write the organisation to the session on login |
# |
May 19th 2021, 13:59 |
ndm |
All events will bubble to the global event manager instance. |
# |
May 19th 2021, 13:59 |
ndm |
Oh wait, you'd need to put it before authentication... but I guess you don't need to access to the identity for this? |
# |
May 19th 2021, 13:58 |
neon1024 |
Although I am using the static EventManager::instance so should be global right? |
# |
May 19th 2021, 13:58 |
neon1024 |
I’ll give it a go :thumbsup: |
# |
May 19th 2021, 13:58 |
neon1024 |
Can the middleware observe a Model.initialize? :thinking_face: |
# |
May 19th 2021, 13:57 |
ndm |
You might want to consider a custom middleware, you have access to the authentication result and the request object, thus the session. |
# |
May 19th 2021, 13:56 |
neon1024 |
Now the Auth middleware is creating the instances without even calling the controller |
# |
May 19th 2021, 13:56 |
neon1024 |
Binding on the Model.initialize worked great, as I had the session in the AppController, so could bind the behaviour and give it the config it needed |
# |
May 19th 2021, 13:56 |
neon1024 |
This all stems from the fact the behaviour, which appends to the query, takes an organisation id from the session |
# |
May 19th 2021, 13:55 |
neon1024 |
Or if I should re-bind the Behaivour to the existing instance in the 4 tables used in the Auth |
# |
May 19th 2021, 13:55 |
neon1024 |
Now I’m wondering how I can either clear those instances after the Auth checks, so the AppController can pickup the Model.initialize |
# |
May 19th 2021, 13:54 |
neon1024 |
You were right in the execution order, but I do not know how the TableRegistry is persisting instances |
# |
May 19th 2021, 13:52 |
ndm |
Behaviors are not singleton-ish like tables, if that is what you are referring to, a new behavior instance is created for each table instance. |
# |
May 19th 2021, 13:47 |
neon1024 |
`$table->behaviors()->get('FilterByOrganisation')->setConfig('organisation', $organisation->toArray());` |
# |
May 19th 2021, 13:47 |
neon1024 |
I assumed i could update a behaviour on an instance in the registry, but doesn’t seem to work |
# |
May 19th 2021, 13:46 |
neon1024 |
Is it more efficient to unload and reload a behaviour or flush the alias from the table registry? |
# |
May 19th 2021, 13:32 |
me1367 |
Don't save it to a database like that. Instead, just grab the latest conversions and cache them for a while. Doing is while you save something (eg. a product) will likely cause inaccurate conversions in a market that always shifts. It could be that 1USD == 1EUR today. But maybe, tomorrow, 1USD == 1,05EUR (and that 5 cents does add up the higher the prices go). |
# |
May 19th 2021, 13:24 |
khalil |
Yes i understand, but i need it in the table class at least, cause i need to modify the price before saving it, the main currency in the db is USD, everything else is basically converted |
# |
May 19th 2021, 13:23 |
khalil |
:rolling_on_the_floor_laughing::rolling_on_the_floor_laughing::rolling_on_the_floor_laughing: it was a stupid question haha |
# |
May 19th 2021, 13:12 |
me1367 |
Correct. You can just add a little method to the utility class that checks for a cached version and if not, pulls the latest one from online (just use the built-in cache expiry for checking when to update). Though, I'd use a cron + command for that instead... Doing it "on-demand" is gonna kill TTFB for the poor bloke that first hits the expired cache :anguished: |
# |
May 19th 2021, 13:08 |
me1367 |
Yes, it's just basic OOP :P I wouldn't use it in table and entity classes though. Primarily controllers and such when it'll actually be needed. Don't bloat your tables and entities. |
# |
May 19th 2021, 12:03 |
khalil |
Thanks |
# |
May 19th 2021, 12:03 |
khalil |
I can! |
# |
May 19th 2021, 12:00 |
khalil |
But can i access this in Table and Entity classes? |
# |
May 19th 2021, 11:59 |
khalil |
Thank you so much!! |
# |
May 19th 2021, 11:07 |
neon1024 |
Especially as currency conversion rates change regularly, the class would probably have some feature to keep those rates updated |
# |
May 19th 2021, 10:59 |
me1367 |
I wouldn't do that in the bootstrap.php because it indeed, can get messy real quick. Instead, I'd create a utility class for that and call that. Something like this: ```namespace MyApp\Utility; use Cake\Routing\Router; class CurrencyConverter { private $user = null; __construct() { // Get the authenticated user $this->user = Router::getRequest()->getSession()->read('Auth.User'); } convert(int $amount) { |
# |
May 19th 2021, 10:59 |
slackebot |
// ... Do your thing here } }``` Then just call it where needed like this: ```namespace MyApp\Controller; use MyApp\Controller\AppController; use MyApp\Utility\CurrencyConverter; class WhateverController extends AppController { public function myAction() { $converter = new CurrencyConverter(); $converted = $converter->convert(12.99); } }``` The class can be called basically anywhere outside the regular CakePHP |
# |
May 19th 2021, 10:59 |
slackebot |
hierarchy. |
# |
May 19th 2021, 10:55 |
ndm |
Instinctively I'd say no, as it sounds like you're introducing lots of global state and hard dependencies. |
# |
May 19th 2021, 10:50 |
khalil |
Hello support! A quick question related to best practices, is it ok to add a global function that needs to be accessed anywhere in the app (models, views and controllers) in config/bootstrap.php ? and is it ok if this function needs to access the authenticated user? I know I can create helpers and components for, but it gets easily messy since this needs to be accessed literally everywhere, and this function is required for the |
# |
May 19th 2021, 10:50 |
slackebot |
site to run (it handles currency conversion based on each logged in or not logged in user) - so I thought bootstrap can be a good place for that, but I'm not sure if it's ok in terms of best practices |
# |
May 19th 2021, 10:45 |
neon1024 |
I put a debug backtrace into the table initialize, and there isn’t a single controller there |
# |
May 19th 2021, 10:41 |
neon1024 |
Thanks for the lead there @ndm I think you’re right. As the Auth is kicking things off from the Middleware, no controllers are called! |
# |
May 19th 2021, 10:41 |
kevin.pfeifer |
i see |
# |
May 19th 2021, 10:41 |
steinkel |
yes |
# |
May 19th 2021, 10:41 |
kevin.pfeifer |
and you load/require that events.php in the bootstrap.php or somewhere else? |
# |
May 19th 2021, 10:40 |
steinkel |
^ I usually define a specific config/events.php and add docblock per event with links to the dispatch line in the code, the reason of this event, any data in/out expected, exceptions raised etc |
# |
May 19th 2021, 10:37 |
kevin.pfeifer |
Talking about Event Listeners: Wouldn’t it suffice to put all the Event Listeners in the `config/bootstrap.php` (or at least put some logic in there to load all of them in there)? In other words: Is there a distinct “recommended place” where to put Event Listeners in general? Or is the answer “it depends” ,:) |
# |
May 19th 2021, 10:24 |
me1367 |
I'll have a look at it, thanks |
# |
May 19th 2021, 10:24 |
ndm |
I think it was this plugin: https://github.com/liqueurdetoile/cakephp-orm-json |
# |
May 19th 2021, 10:23 |
ndm |
Yeah, no, it's going to be a bit work :) |
# |
May 19th 2021, 10:20 |
me1367 |
Yea, just wondered if the ORM was able to do this all for me or not. Would make it a lot easier since now I have a bunch of data in different tables to keep said tables smol (column wise). |
# |
May 19th 2021, 10:19 |
ndm |
I remember seeing a plugin that supports generating all sorts of JSON Path syntax, but IIRC everything that the path syntax can do, can be done with the equivalent function calls too. |
# |
May 19th 2021, 10:17 |
me1367 |
r.i.p |
# |
May 19th 2021, 10:17 |
neon1024 |
Good point, my `auth` finder does use some of these |
# |
May 19th 2021, 10:17 |
ndm |
@me1367 JSON functions can be generated like any other methods via the functions builder... aside from that, there's no specific JSON support. |
# |
May 19th 2021, 10:13 |
ndm |
@neon1024 Maybe whatever models are involved, are being constructed earlier, maybe in your policies, and/or for resolving identities... Put a debug or a breakpoint in their `initialize()` method to figure whether initialization happens before the listener is registered. |
# |
May 19th 2021, 10:11 |
me1367 |
Quick question... Does CakePHP's ORM have support for this feature in MariaDB? https://mariadb.com/database-topics/semi-structured-data/ |
# |
May 19th 2021, 10:11 |
neon1024 |
As we deployed the 4.0.10 upgrade to prod already and it’s fine |
# |
May 19th 2021, 10:10 |
neon1024 |
It worked fine until I added the Authorization plugin |
# |
May 19th 2021, 10:10 |
neon1024 |
It’s the first thing in the method |
# |
May 19th 2021, 10:10 |
neon1024 |
I am using `\App\Controller\Admin\AppController::initialize` |
# |
May 19th 2021, 10:10 |
ndm |
Might depend on when you register that listener, the event will only be triggered once for each table alias when its constructed, so if you register it too late, it won't be invoked. |
# |
May 19th 2021, 10:07 |
neon1024 |
I added a `pr()` to the `setCurrentOrganisation` method to see how often it was called. Unless Crud doesn’t instantiate the table classes any more :thinking_face: |
# |
May 19th 2021, 10:06 |
neon1024 |
The next thing.. always another.. is that `EventManager::instance()->on('Model.initialize', [$this, 'setCurrentOrganisation']);` doesn’t seem to be called for any Table classes any more except `Requests` and `Panels` from DebugKit. |
# |
May 19th 2021, 10:05 |
neon1024 |
Yes, super, thanks very much @kevin.pfeifer |
# |
May 19th 2021, 09:40 |
kevin.pfeifer |
it should then redirect without the need to extend the middleware and use your own try catch block |
# |
May 19th 2021, 09:30 |
neon1024 |
:thumbsup: |
# |
May 19th 2021, 09:22 |
kevin.pfeifer |
as stated in https://book.cakephp.org/authorization/2/en/middleware.html#handling-unauthorized-requests |
# |
May 19th 2021, 09:22 |
slackebot |
],``` |
# |
May 19th 2021, 09:22 |
kevin.pfeifer |
I just checked your screenshot again and it seems you misplaced the `exceptions` array. It should be inside the `unatuhorizedHandler` array like that ``` 'unauthorizedHandler' => [ 'className' => 'Authorization.Redirect', 'url' => '/users/login', 'queryParam' => 'redirectUrl', 'exceptions' => [ MissingIdentityException::class, OtherException::class, ], |