Jump to content

Custom class autoloading in templates


TomPich
 Share

Recommended Posts

Hi guys,

I know this has been asked already a few times here, but I couldn’t find a working solution for me...
Working on a quite big project and I feel it can get messy quite easily. I need to tidy my code a little but.

So here is the situation: I have quite a lot of data handling to do in the front side of my website. This involves querying on an external DB and processing the data.

I started to write some methods in a DefaultPage class and that works well, but this will end with a very big file with tens of methods not related to the same logic.
What I would like, is to have a src/ folder in my site/ folder, with all the custom classes in relevant subfolders.

I tried this:

// in site/init.php
$namespace = 'ProcessWire\\App';
$classLoader = $this->wire('classLoader');
if (!$classLoader->hasNamespace($namespace)) {
    $srcPath = 'src/'; // target folder is /site/src
    $classLoader->addNamespace($namespace, $srcPath);
}

Inside this site/src/ folder, there is a MyClass.php file, with the class MyClass

namespace App;

class MyClass
{
    const hello = "hello";
}

And in my template file:

namespace ProcessWire;
use App\MyClass;
<?= MyClass::hello  ?>

But I can’t get class loaded... 😔
I tried variation with namespace and use, but it didn’t work...
Any help please ?
Thank you!

 

Link to comment
Share on other sites

Hi,

I tried the same a while ago and it appears PW is forcing custom classes to be in site/classes directory. The code seems to be in ProcessWire.php:

$cfg['paths']->data('classes', $cfg['paths']->site . "classes/");

Maybe you can overwrite this in your own config.php? Not sure if this is a good idea. ^^

But these classes dependencies can be in any package in site/templates/YourPackage (parent/abstract classes, Traits...).

On my side the code I put in page classes is only related to the model part (getting/setting data), I don't use them to do rendering or controller stuff. The main part of the code is in my packages in site/templates. So I'm not really annoyed by the fact they have to be in the same directory, but I admit it would be better to put them in their related package.

Edited by da²
  • Thanks 1
Link to comment
Share on other sites

<?php 
// site/init.php
namespace ProcessWire;

if (!defined("PROCESSWIRE")) die();

$classLoader = $wire->classLoader;
$classLoader->addSuffix("Service", __DIR__ .  '/services');
$classLoader->addSuffix("Form", __DIR__ .  '/forms');
$classLoader->addNamespace("ProcessWire", __DIR__ .  '/utils');

 

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

Hey guys,
@da², @fliwire, thank you for your help. It pushed me in the right direction.
I got my things done and wanted to share with whoever would find that useful.
Here was my need: a specific folder to store some classes, usable easily across templates (ie "use Class" and not "include ...".
These classes wouldn’t fit (IMO) into a DefaultPage class for separation of concerns matter.
And I didn’t want to try to develop a module, seemed an overkill and time is running...

I decided to store my custom classes files into some subfolders of the /site/classes folder. At first, I used a /site/src folder (and it worked), but as the classes folder already exists, I thought it made more sense to use this folder. This is my architecture

├── site/
│   ├── assets
│   ├── classes/
│   │   ├── Repository/
│   │   │   ├── AbstractRepository.php
│   │   │   └── ContactRepository.php
│   │   ├── DefaultPage.php
│   │   ├── HomePage.php
│   │   └── Utils.php
│   ├── modules
│   └── templates
└── wire

For autoloading use PW autoloader, in put in init.php

$classLoader = $wire->classLoader;
$classLoader->addNamespace("ProcessWire", __DIR__ .  '/classes');

Then the magic happens. My ContactRepository.php, for example :

namespace ProcessWire\Repository;

use ProcessWire\Utils;

class ContactRepository extends AbstractRepository
{
	// my stuff here...
}

and when I need it in a template file :

namespace ProcessWire;
use ProcessWire\Repository\ContactRepository;

$contactRepo = new ContactRepository;
$speakers = $contactRepo->getAllSpeakersAndModerators();

Remarque:
In the init.php file, I tried with and without

$classLoader->addSuffix("Repo", __DIR__ .  '/classes/repositories'); // does not change anything
$classLoader->addPrefix("Repo", __DIR__ .  '/classes/repositories'); // does not change anything

I can't really grasp in which situation this useful, and the doc is quite short... 😅
So with this file structure, I can really get a step further in logic handling and code maintenance.
Hope someone will find this useful.
Cheers

  • Like 4
Link to comment
Share on other sites

4 hours ago, TomPich said:

For autoloading use PW autoloader, in put in init.php

$classLoader = $wire->classLoader;
$classLoader->addNamespace("ProcessWire", __DIR__ .  '/classes');

If you need to do it even earlier than using init.php:

https://processwire.com/talk/topic/26875-page-classes-with-traits/?do=findComment&comment=222333

also see: https://github.com/processwire/processwire/blob/3cc76cc886a49313b4bfb9a1a904bd88d11b7cb7/wire/config.php#L1756

 

  • Thanks 1
Link to comment
Share on other sites

1 hour ago, TomPich said:

If I don’t use it, ProcessWire is apparently not able to import classes in the Repository Subfolder

Hmm, I don't need it on my side. Are you using the correct namespace? I added a directory Test in site/classes/, inside a class Foo with this namespace:

<?php namespace ProcessWire\Test;
class Foo{}

And in a template.php I instanciate it:

use ProcessWire\Test\Foo;
$test = new Foo();

If you're using the same code, maybe it comes from my composer.json but I would be surprised. I updated the composer.json, but only to add my own namespaces.

** looking my composer.json and... Hoooooo yes I know why... 😁 **

  "autoload": {
    "files": [
      "wire/core/ProcessWire.php"
    ],
    "psr-4": {
      "Rfro\\Rfrorga\\ProcessWire\\": "site/templates/",
      "ProcessWire\\": "site/classes/",
      "Rfro\\Rfrorga\\WebApi\\": "webApi/",
      "Rfro\\Rfrorga\\Cron\\": "cron/"
    }
  },


I don't remember why but I added a namespace for site/classes. 🤔 Probably for the same problem as you... Could it be a cleaner way to solve your issue?

Edited by da²
  • Like 2
Link to comment
Share on other sites

9 hours ago, fliwire said:
// suffix is Repository not Repo.

$classLoader->addSuffix("Repository", __DIR__ .  '/classes/Repository');

Yes... I tried Repository first, then I tested with another suffix (Repo), and it didn’t change anything (and I then commented it out...). The addnamespace does all the job.
So I don’t get what this addSuffix does. My guess is that it allows better performance when there are a lot of classes, pointing out the sub-folder to look into. But then, so does the

namespace ProcessWire\Repository;

I can't figure it out...

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...