Why in laravel do you need to specify classes in composer.json - laravel

Why in laravel do you to specify classes in composer.json? I thought composer is a program that manages your project's dependencies, yet it's also used to map your controller classes for instance. Why is that?

You don't need to specify your classes in composer.json. Sure you can do that if you want but for most of the cases there is no need to do so.
Let's take a look at the autoload section of Laravels default composer.json
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php"
]
},
The classmap basically means autoload everything that's specified here. If it's a directory (like "app/controllers") it will load all classes within the folder. recursively.
So just because you want to move your controllers to subdirectories in app/controllers doesn't mean you have to change anything in composer.json
You have to do one thing though, run composer dump-autoload. You see, composer creates a file where it stores the classes and the actual file, that contains the class. You can find this file at vendor/composer/autoload_classmap.php.
The entries look like:
'IndexController' => $baseDir . '/app/controllers/IndexController.php
If you now move IndexController.php to app/controllers/foo the application will still try to include it from app/controllers until you run composer dump-autoload which will regenerate autoload_classmap.php.

Related

How to add code to vendor folder manually

I need to clone a project of my company on my local machine.
Problem: There is a module required in the main composer.json from our old agency which does not exist anymore, so I don't have the access keys needed to download it, BUT I have the latest code of that module.
The composer.json of that module says its name is "randomagency/import", so I added the code at vendor/randomagency/import. Then I removed the module from the main composer.json.
But I get this error:
Class 'RandomAgency\ProcessPipelines\Helper\Condition\AbstractCondition' not found#0 /var/www/company/src/vendor/composer/ClassLoader.php(444): include()
#1 /var/www/company/src/vendor/composer/ClassLoader.php(322): Composer\Autoload\includeFile()
#2 [internal function]: Composer\Autoload\ClassLoader->loadClass()
#3 [internal function]: spl_autoload_call()
My colleague told me that I need to add the module in the main composer.json under the autoload section, but Im not sure how exactly it works.
The best approach to solve it would be to create a new composer package and replace the agency URL in composer.json with my own, but I need a quick & dirty method for now.
Don't put it into vendor (as it is expected to get removed in time and the name of the vendor-dir is a configuration detail), so technically you have already put it in the wrong location. However, this is easy to recover from, just use a different path within the project root directory, e.g. create a directory where you put packages into (pkg in the following). This works already with autoloader configuration only (if that library requires no other configuration to work):
PSR-4 Autoloader (only) from Project Directory
Then add that path to the projects autoload configuration (see autoload (Composer configuration schema) - Autoload mapping for a PHP autoloader.:
{
"autoload": {
"psr-4": {
"RandomAgency\\Import\\": "pkg/random-agency-import-4.2"
}
}
}
It's good practice you have a single folder for the package - not two as in <vendor>/<package> pattern - and append an identifier of the package version.
Require as Composer Package from Project Directory
Better thought, if that package still contains the composer.json configuration file, add it as a path repository and require it with a version constraint that matches the ./pkg/random-agency-import-4.2/composer.json#/version or * if the "version" member is missing:
{
"require": {
"random-agency/import": "*"
},
"repositories": [
{
"type": "path",
"url": "./pkg/random-agency-import-4.2"
}
]
}
You then don't need to configure the autoloader manually as it is taken from the libraries composer.json configuration. This is equally true for any other configuration part incl. the dependencies of the library.
The great benefit of the path repository is, that is accepts relative paths to your projects configuration file (composer.json).
Compare with your previous understanding:
The best approach to solve it would be to create a new composer package and replace the agency URL in composer.json with my own, but I need a quick & dirty method for now.
You either have solved it by now (the package you require has a composer.json file) or you still need to add the package information, but you don't need to create a new package or exchange URLs.
Package Repository: Inline a Composer Package from Project Directory
That is because you can inline the package as well in Composer with the package repository that now has the path and if you look at /repositories/0/package closely, you can see that this is another composer.json document, just inline at that place (and the /dist member is new):
{
"repositories": [
{
"type": "package",
"package": {
"name": "random-agency/import",
"version": "4.2.0",
"dist": {
"type": "path",
"url": "./pkg/random-agency-import-4.2"
},
"autoload": {
"psr-4": {
"RandomAgency\\Import\\": ""
}
}
}
}
],
"require": {
"random-agency/import": "4.2.0"
}
}
When you do updates etc., Composer then will install the packages files into the vendor folder (where-ever it may be, and regardless how often you remove it).
If - and only if - there is no ./pkg/random-agency-import-4.2/composer.json file, you would need to create a "package", as that is the composer.json in the end.
Alternative: compser.json within the package directory
Similar as you added the autoload part to composer.json and as an alternative to the package repository, you can create the ./pkg/random-agency-import-4.2/composer.json file your own, then the path repository already suffices and it is perhaps easier to configure / maintain as the roots project composer.json file does not expand all the package details.
But that is your preference, just showing that you have a couple of options within the project itself without creating outside / "new" packages.
Figured it out. Had to add it like this:
"autoload": {
"psr-4": {
"RandomAgency\\Import\\": "vendor/random-agency/import"
},

Modified vendor files, Laravel

I'm currently using Laravel 7.xx, and during working on my project, I change some vendor files, modify it
Because few things doesn't work for me, so without any option left, I have to modify some vendor files.
In case if I ever want / need to update my laravel / packages version, will the modified vendor files revert back to original state and I lost all my modified code?
Thanks in advance
Yes! whenever you do composer install or composer update, your modified vendor files will revert back. So, you never should edit any vendor files.
What you can do is extend those specific classes to new files of your own
Make a new file of the class that you are overriding : (e.g : app/Overrides/MyClass.php)
in your composer.json
"exclude-from-classmap": [
"vendor/pathtotheYourVendorFileThatYouAreOverriding.php"
],
"psr-4": {
"App\\": "app/",
"Illuminate\\": "app/Overrides/"
}

Composer private package issue

I have created a private composer package in packagist.com but when I use
composer require command to fetch it. My package coming under vendor folder which is on root.
But I want it to be in app/code folder. Is there any parameter for composer.json where I can set app/code, so it will come under app/code/.
Yes there is. According to the composer documentation, if your project uses the PSR-4 specification for loading classes, then adding the snippet below to the composer.json file would work.
NB: You will need to change ProjectNameSpace to the base namespace for your project.
...
"autoload": {
"psr-4": {
"ProjectNameSpace\\": "app/code"
}
},
...
Theoretically. You can't composer will always place the code in vendor directory. The code is external and therefore can be updated only by composer. It must not be in app/code as only code you modify for the project should be in app/code.
If you wish to make a Magento project, you should have the fallowing files in the versioning tool.
app/*
composer.json
composer.lock
.htaccess
index.php
The other files will be handled by composer.
But if you really need to do it, but I don't see any reason to do so, then you could use a post-update & post-install script to move the code. But it's a very bad idea.

Difference between PSR-4 and classmap autoloading?

In regards to Laravel, I got a question about Composer autoloading i.e. the difference between "psr-4" and "classmap" autoloading.
1 difference that I know is PSR-4 does not need repeated dumpautoload for every changes or addition of new files whereas classmap needs dumpautoload for every change in existing files containing classes or addition of new file in specified directory.
PSR-4 standard requires of you a strict filesystem structure based on namespaces. Say you have an app in src directory with App namespace, then all sub-namespaces will mirror subdirectories and class names will be the same as file names without the .php extension.
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
src/
Foo/
Bar.php <---- App\Foo\Bar class
Baz.php <---- App\Baz class
The autoloader then "knows" where to look for the class of a certain fully qualified name and therefore doesn't require the dump-autoload command to sniff files for classes.
Performance issues are then solved with composer dump-autoload --optimize-autoloader flag, or -o, which will generate class map a similar way the classmap autoloading does.
On the other hand, classmap autoloading does not require you to have almost any certain file or directory structure, it will recursively go through .php and .inc files in specified directories and files and sniff for classes in them.
{
"autoload": {
"classmap": ["src/", "lib/", "Something.php"]
}
}
Those classes are then added to a list (cached in a PHP file in vendor/composer directory) which is used for autoloading.
Any new class then must be added to that list by running composer dump-autoload command.

Autoloading a third party library via Composer's autoloader?

I've got a little Laravel4 project into which I need to incorporate a third party library. This library is not available via GIT or Packagist (only from the vendor), so I downloaded it into my vendor directory (had to add some custome vendor and package directories).
Rather than just include()-ing it as suggested by the vendor's documentation, I'm hoping to just use the existing Composer autoloader, and can use a hand figuring out what I'm doing wrong.
My dir tree is like this
path/to/project
|__ app
|__ vendor
| |__ merchantcompany
| |__ client
| |__ src
| |__ client.php
|__ blah
|__ blah
And I updated my compser.json to include:
"autoload": {
"psr-4": {
"MerchantCompany\\": "vendor/merchantcompany/client/src"
},
...
I also tried "MerchantCompany\\": "src", but to no avail.
WHAT am I missing?
NOTE: The class from the vendor is not namespaced.
Am I under the correct assumption that this is fine, or should I be adding a namespace to the class script?
You are currently doing it wrong. You manually inject the package into a folder that is managed by Composer (which can wipe that directory if seen fit), and you incorporate the autoloading sort of into your own code.
Composer offers a way to add the needed metadata to projects which do not have them. This is the "package" type of repository described in https://getcomposer.org/doc/04-schema.md#repositories
If you look at the example given there for Smarty, you see that you basically need to add a key "type" with value "package" and a key "package" with the content of the composer.json file you'd like to see contained inside the project.
In this case there is a version tag added (in sync with the version of Smarty being used, just in case some later versions make use of Composer and Packagist, which Smarty does since some version 3.1.x) to allow Composer to reference this version, a name for that package (both values can be arbitrarily made up if you doubt you'll ever get that software with Composer support), and a URL to download the code from (you don't have to provide both a ZIP download AND a repository if you don't know them).
The thing that is missing is the definition of autoloading, which can be added just the same way as everywhere else. If nothing else works, use "classmap". Composer will then scan all files for occurrences of classes, interfaces and traits and will generate an array containing the accompanying filenames. You can however also use PSR-0 or PSR-4 if the code conforms to that standard.
Note that PSR-4 can only be used for classes using namespaces! Without namespaces, you must use either PSR-0, or classmap. From the short piece of directory listing I doubt the code is compatible with PSR-0, so just use classmap for quick results.
As a suggestion:
"repositories": [
{
"type": "package",
"package": {
"name": "merchantvendor/client",
"version": "1.0.0",
"dist": {
"url": "http://example.com/zip-download-url.zip",
"type": "zip"
},
"autoload": {
"classmap": ""
}
}
}
],
"require": {
"otherstuff": "...",
"merchantvendor/client": "1.0.0"
}
PSR-4 is a namespace-based autoload standard. You won't be able to use it if your vendor's PHP package does not utilize namespaces.
If the package's files are class based (not procedural functions like a helpers file), then you can use Composer's classmap autoloader instead. Otherwise you can use the files autoloader, which basically includes the file on every request, regardless if you use it or not.
Note that since you are defining the path to these files manually, they do not need to be in your vendor folder. I'd actually recommend that you put the library in a different, non-.gitignore folder, since it is an external dependency that you can not automatically include into your project via Composer.
Of course you can always nag your vendor into updating their package to be more compliant with things like PSR-4 and namespaces. :)

Resources