How do you manage your dependency libraries? - php

How do you manage your dependency libraries? I separate my project into a bunch of libraries, because these libraries are also used in several other projects. In the beginning, I make each of them as Git repo, and I use Git submodule to manage them. Soon it becomes a nightmare. Once I make some changes, I have to commit in submodule, this is tedious, and need tremendous work.
I am wondering how Sylius did that, they keep each bundle as Git and Packagist repo, but they don't use Git or Composer to manage their own bundles.

The best way is to use composer to manage your dependencies and to autoload all your classes.
The first step in order to achieve that is to prepare all your components to be ready for composer, so each one of your dependencies will have their own composer.json at the root. A basic configuration may look like that:
{
"name": "your/component-name",
"description": "your description",
"license": "proprietary",
"authors": [
{
"name": "Your name",
"email": "you#mail"
}
],
"autoload": {
"psr-4": {
"Your\\Complete\\Namespace": "src/"
}
},
... etc ...
}
The name field is the name you will use to load the dependencies in your main project
The autoload section is very important since it will determine the base namespace of all your classes. When you'll import your dependencies in your main project, you will access to your component classes via this namespace.
When your dependencies will be ready, you will prepare your main project to load them via composer. So basically, the composer.json structure of this project will look quite the same as the previous with more options in order to load your dependencies
{
"name": "your/project-name",
"description": "your description",
"license": "proprietary",
"authors": [
{
"name": "Your name",
"email": "you#mail"
}
],
"autoload": {
"psr-4": {
"Your\\Project\\Namespace": "src/"
}
},
"require": {
"your/dependency1-name" : "dev-master",
"your/dependency2-name" : "dev-master",
....
},
"repositories": [
{
"type": "git",
"url": "https://github.com/the-git-url-of-your-project1"
},
{
"type": "git",
"url": "https://github.com/the-git-url-of-your-project2"
}
]
... etc ...
}
each line of the require part will allow you to configure all the dependencies you want to load (its the name part of the dependency composer.json) in which version (dev-master or the number of the tag if you have some).
repositories part: Except if your dependencies are on packagist (https://packagist.org/), you'll have to add the repository of your dependencies (it could be github, bitbucket etc...). It the same url you can find in the clone section of your repo.
This is the steps you have to follow in order to manage your dependencies with composer. Obsviously, you really need to check the documentation to adapt it to your needs cause its just an basic overview of what you can do with composer.
when everything is ready, a composer install should load your dependencies in a vendor directory and all your classes available by its namespace.
You can look at the documentation for more options:
https://getcomposer.org/doc/
And this usefull Cheat Sheet
http://composer.json.jolicode.com/

I see in comment (i can't add comments) that you want to commit all changes made in your main application to all bundles.
You can look at the no-api option of composer:
"repositories": [
{
"type": "git",
"no-api": true,
"url": "https://github.com/the-git-url-of-your-project1"
}
]
composer will do a git clone when you do a composer install
Another solution without the use of composer is to use git submodules

Related

PHP composer: Update local path repository dependency

I have a project which requires a bunch of local path repositories. Those local path repositories partly require each other and also remote repositories.
For example one of the local repo composer.json looks like this:
{
"description": "",
"type": "neos-project",
"name": "mapo/campaign",
"repositories": [
{
"type": "path",
"url": "Source/Mapo.NodeTypes"
}
],
"require": {
"mapo/nodetypes": "*"
},
"autoload": {
"psr-4": {
"Mapo\\Campaign\\": "Classes/"
}
},
"extra": {
"neos": {
"package-key": "Mapo.Campaign"
}
}
}
The mapo/nodetypes local package then requires also a private remote repository:
{
"description": "",
"type": "neos-project",
"name": "mapo/nodetypes",
"minimum-stability": "dev",
"repositories": [
{
"type": "git",
"url": "url to private repo.git"
},
{
"type": "path",
"url": "../Mapo.Somepackage"
},
],
"require": {
"mapo/privateproject": "*",
"mapo/somepackage": "#dev"
},
"autoload": {
"psr-4": {
"Mapo\\NodeTypes\\": "Classes/"
}
},
"extra": {
"neos": {
"package-key": "Mapo.NodeTypes"
}
}
}
Now I need to test a new feature in mapo/privateproject. So I created a new branch in the private repo called issue0815 and made my changes. I also created a new branch issue0815 in the main mapo project (which has the local path composer.json changes locally).
I updated the dependency of mapo/nodetypes to "mapo/privateproject": "dev-issue0815",.
My problem is that, no matter which command, composer refuses to install the issue0815 branch for the private repository. At first composer complained, that it cannot install the dependency dev-issue0815 because the composer.lock prevented it. So I removed all usages of the private repo and the mapo/nodetypes from my composer.lock.
What surprised me the most, was that composer recovered the original composer.lock file. It just completely ignored my current local main project branch - which has the modification for the local path repo composer.json files and just required the contents from the master branches.
So, how can I update a dependency of a local path repo which needs a specific branch from a private repo?
This one took me quite a while, but the reason why my changes to composer.json have been completely ignored including also when I deleted the composer.lock was that, composer ignores your changes if it concludes, that the packages from vendor/composer/installed.json are correct.
From https://github.com/composer/composer/issues/4312#issuecomment-191488570:
Right now Composer prefers installed packages, derived from the installed.json file. So as long as the replacement is valid, it will indeed keep generating the same solution and thus lock. This is just because that's how Composer works internally.
To 'reset' it completely and make it rethink its installed packages (before it automatically 'corrects' because of package updates) you'd have to remove vendor/composer/installed.json as well as the lock. This will trigger a complete reevaluation of dependencies and reinstall. You could also just go for the full cleanup then and delete the entire vendor directory.
Thats also the reason why it is often suggested to remove the vendor directory, I think.

Load two custom libraries

I have some problem by using Composer to load a custom library from another custom library
I have 2 custom libraries called "ia/audit_trail" and "ia/flash". And "ia/audit_trail" needs "ia/flash" to work.
audit_trail : https://github.com/pierrererot/audit_trail
flash : https://github.com/pierrererot/flash
So, I have the require property set for calling another one. Nothing special, BUT, when I run a simple composer update -vvv in my main project, I got this error :
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Installation request for ia/audit_trail_component ~1.0.0 -> satisfiable by ia/audit_trail_component[1.0.0].
- ia/audit_trail_component 1.0.0 requires ia/flash_component ~1.0.0 -> no matching package found.
Potential causes:
- A typo in the package name
- The package is not available in a stable-enough version according to your minimum-stability setting
see https://getcomposer.org/doc/04-schema.md#minimum-stability for more details.
- It's a private package and you forgot to add a custom repository to find it
Read https://getcomposer.org/doc/articles/troubleshooting.md for further common problems...
BUT, if I put these two librairies directly into my main project (so if one librairy doesn't need another librairy), it works !.
Here is the composer.json of my main project :
{
"require": {
"ia/audit_trail_component": "1.0.0"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/pierrererot/audit_trail.git"
}
]
}
All right. So I did require my custom "audit_trail" library. So now, here is the composer.json of my custom "audit_trail" library :
{
"name": "ia/audit_trail_component",
"version": "1.0.0",
"type": "library",
"require": {
"ia/flash_component": "1.0.0"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/pierrererot/flash.git"
}
],
"minimum-stability": "dev"
}
All right. So I did require my custom "flash" library. And then, here is the composer.json of my custom "flash" library :
{
"name": "ia/flash_component",
"version": "1.0.0",
"description": "Flash Component",
"type": "library",
"minimum-stability": "dev"
}
As you can see, everything seems ok in my composer files, so I don't understand what I missed.
==> Does anyone have a clue please ?
Before you ask, I precise these things :
Both libraries have a "dev" and a "master" branch pushed on their Git repositories
Both libraries have a minimum 1.0.0 tag pushed on their Git repositories
repositories setting is root-only - Composer will ignore this setting for all dependencies and use only these repositories defined in your main project.
Repositories are only available to the root package and the repositories defined in your dependencies will not be loaded. Read the FAQ entry if you want to learn why.
https://getcomposer.org/doc/05-repositories.md#repository
So you need add all necessary repositories into composer.json of your main project:
"repositories": [
{
"type": "vcs",
"url": "https://github.com/pierrererot/audit_trail.git"
},
{
"type": "vcs",
"url": "https://github.com/pierrererot/flash.git"
}
],

Use forked repo on github via composer as dependency

I have a project that uses socalnick/scn-social-auth-doctrine-orm. This module further depends upon:
socialnick/scn-social-auth
Which further depends upon
hybridauth/hybridauth
So ORM Depends on->Social-Auth which depends on->Hybrid
In order for my application to work, I required some changes in these two modules (1) and (2). I forked these modules to my git account and made changes as per my requirement. In my application composer.json I am just putting socalnick/scn-social-auth-doctrine-orm as requirement.
How can I manage composer.json so that socalnick/scn-social-auth-doctrine-orm get my forked modules instead of default modules.
You should override that dependencies in your composer.json.
{
"require": {
"socalnick/scn-social-auth-doctrine-orm": "*",
"socialnick/scn-social-auth": "*",
"hybridauth/hybridauth": "*"
},
"repositories": [
{
"type": "git",
"url": "https://github.com/USER/scn-social-auth.git"
},
{
"type": "git",
"url": "https://github.com/USER/hybridauth.git"
}
]
}
In your fork you could add a tag that matches the requirements of the main package, or use a branch with a version number alias:
"socialnick/scn-social-auth": "dev-mybranch as 2.1.0",
Reference blog post from mnapoli

Composer, Laravel and local packages

My issue is I have a package which isn't a repository and I am trying to get it to play nice with Laravel and composer. It is still located under the vendor folder, the only issue is that if I simply set:
"psr-0": {
"Test\\Test": "vendor/test/test/src/"
}
This will load the service provider but none of the controllers etc will autoload. What is the correct way to implement a package with larval that does not have it's own repository. Or does this go against the nature of packages and this should simply be structured under the applications controllers.
The package was created by me using workbench but I found i did not really need this as a separate repository but it would still be good to keep it as a package. Therefore the structure is exactly the same as a regular package:
vendor
testvendor
testpackage
public
src
tests
.gitignore
composer.json
phpunit.xml
UPDATE:
As a solution for the time being I am using:
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php",
"vendor/package"
]
},
As an entry in the class map. Looking forward I will probably refactor this into the app folder or create a repository for this package.
If you have some classes that you're calling "package", you're not supposed to add those files to your vendor folder. This folder is managed by composer and at any time you might loose it. Create a subfolder in your application and put those files there.
You have to be sure your PSR-0 autoloading will work for every single file in your folder structure. So, if your root is vendor/test/test/src/ and your namespace is
Test\\Test
All your files must be in
vendor/test/test/src/Test/Test/ClassFileName.php
PSR-4 is easier to deal and understand, this
"psr-4": {
"Test\\Test\\": "vendor/test/test/src/"
}
Means that your files would have to be like:
vendor/test/test/src/ClassFileName.php
Doublecheck your namespaces. It's easy to make mistakes when using namespaces with PSR-0 and remember that
composer dump-autoload
Must be ran every time you change things in composer.json or create new files. If it's a simple class autoloading, every time you create a file, if it's a PSR-X autoloading, everytime you create or update a namespace in your composer.json file.
If what you have is is really a package you should use Composer: when your package is structured as a composer package (check Laravel's composer.json as an example), the correct way of adding it to your application, if it's not list in Packagist, is via repositories.
You can have (non-packagist) packages in a public VCS repository:
{
"require": {
"monolog/monolog": "dev-bugfix"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/igorw/monolog"
}
]
}
You can have (non-packagist) packages in a protected by password VCS repository (git, bitbucket...):
{
"require": {
"vendor/my-private-repo": "dev-master"
},
"repositories": [
{
"type": "vcs",
"url": "git#bitbucket.org:vendor/my-private-repo.git"
}
]
}
You can have your packages zipped in your hard drive and load them via the artifact repository type:
"repositories": [
{
"type": "artifact",
"url": "path/to/directory/with/zips/"
}
],
Though #Antonio Carlos Ribeiro's answer is really nice, I had problem with installing custom packages locally(which is also stated in the last part of his answer)
Let's assume this is the directory structure of the package we are trying to install:
D:/test_pack
src/
composer.json
If you do not want to upload your custom package (that most likely you have developed, yourself) to online repositories you can use one of the following two methods:
Method I
(You have to specify version for your package, otherwise you'll get this error: The requested package could not be found in any version, there may be a typo in the package name.)
1) In composer.json, Add version to your package. your package's json should look something like this:
{
"name": "gandalf/test_pack",//This is your package's name
"description": "some desc",
"version": "1.0.0",//This is the version that you have to specify
"authors": [
{
"name": "gandalf the grey",
"email": "fake#yahoo.com"
}
],
"minimum-stability": "dev",
"require": {
"laravel/framework": "~5.4"
},
"autoload": {
"psr-4": {
"Gandalf\\BotPack\\": "src/"
}
} }
2) zip your package(let's assume the zip file is in D:/test_pack/test_packa.zip)
3) In laravel's composer.json add your package name (in our case gandalf/test_pack into require part of json) and add the repository array to the composer.json file and in that array specify the directory in which your package's zip file exists(in our case D:/test_pack) . like this
{
...,
"require": {//adding our package name to laravel's composer.json
...,
"gandalf/test_pack": "*"//package's name
},
...,
"repositories": [
{
"type": "artifact",
"url": "D:/test_pack"
}
]
}
Method II(My Favorite method, You have to initialize your package directory as git local repository using git init and then git add . and git commit -m "your message")
1) initialize the package directory as git directory and commit all your changes to the local repository
(let's say D:/test_pack is the directory that contains your package(src/ directory and composer.json))
go to D:/test_pack directory and run these commands
git init
git add .
git commit -m "your message for this commit"
2) In your packages composer.json file add minimum-stability
{
"name": "gandalf/test_pack",
"description": "some desc",
"authors": [
{
"name": "gandalf the grey",
"email": "fake#yahoo.com"
}
],
"minimum-stability": "dev",//setting minimum-stability
"require": {
//dependencies that your package needs
},
"autoload": {
"psr-4": {
"Gandalf\\BotPack\\": "src/"
}
}
}
3)In laravel's composer.json file require the "dev-master" of your package
{
...,
"require": {
...,//some dependencies that laravel needs
"gandalf/test_pack": "dev-master"//requiring dev-master from repository
},
"repositories": [
{
"type": "git",
"url": "D:/test_pack"//path of the local repository directory which contains your package
}
]
}
To any Laravel project load local packages. which is stored in your machine.
In laravel's (Project) composer.json file add
"autoload": {
"psr-4": {
"YourPackage\\Namespace\\": "./local_Package_path/src"
}
},
and fire command in Laravel Project directory
composer dump-autoload
Optional
If package is still not available in your Project. then
Register your package's Service Provider.
To register your service provider, you just need to add an entry to the array of service providers in the config/app.php file.
'providers' => [
/*
* Laravel Framework Service Providers...
*/
...
YourPackage\Namespace\PackageServiceProvider::class,
],
Hope now your package loaded successfully in your laravel project.

Use Composer without Packagist

Say for instance you want to use a bundle from someone else, but want to do some modifications. So you do your modifications in some new branch, and configure comspoer.json like:
{
"require": {
"sylius/assortment-bundle": "dev-soft-deleteable-products-disabled"
},
"repositories": [
{
"type": "package",
"package": {
"name": "sylius/assortment-bundle",
"version": "1.0",
"autoload": { "psr-0": { "Sylius\\Bundle\\AssortmentBundle": "" } },
"target-dir": "Sylius/Bundle/AssortmentBundle",
"source": {
"url": "https://github.com/umpirsky/SyliusAssortmentBundle.git",
"type": "git",
"reference": "soft-deleteable-products-disabled"
}
}
}
]
}
This works with master branch, but with custom branch it gives: The requested package sylius/assortment-bundle dev-soft-deleteable-products-disabled could not be found.
Any idea?
You should really be using a VCS repository instead of the package repository. Package is for when there is no composer.json and you want to specify it inline instead. In your case there is a composer.json, so you can use the VCS repo, like so:
"repositories": [
{
"type": "vcs",
"url": "https://github.com/umpirsky/SyliusAssortmentBundle"
}
]
Composer will in this case use the GitHub API to fetch the branch names and check if the version dev-soft-deleteable-products-disabled exists. If it does, it will clone the repository and check out said branch.
Hopefully if you do this as a side effect your problem will be fixed as well.
For more information read the docs chapter on repositories.
Satis can be used as a micro version of Packagist - allowing you to centrally control your Composer dependancies for private repositories.
Composer Guide to Satis Usage

Categories