Use Doctrine Skeleton Mapper for the object mapper layer.

This commit is contained in:
Jonathan H. Wage
2018-09-19 06:57:18 +01:00
parent d0a1ee0b3b
commit 9fe18212be
104 changed files with 4929 additions and 1443 deletions

2
cache/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

View File

@@ -13,9 +13,13 @@
"require": {
"php": "^7.1",
"algolia/algoliasearch-client-php": "^1.27",
"cache/doctrine-adapter": "^1.0",
"doctrine/inflector": "^1.3",
"doctrine/rst-parser": "dev-master",
"doctrine/skeleton-mapper": "dev-master",
"erusev/parsedown": "^1.7",
"knplabs/github-api": "^2.10",
"php-http/guzzle6-adapter": "^1.1",
"scrivo/highlight.php": "v9.12.0.4",
"symfony/config": "^4.1",
"symfony/console": "^4.1",

1949
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@ parameters:
Layer (DBAL) it is built upon.
doctrine.website.google_analytics_tracking_id: ''
doctrine.website.who_uses_doctrine:
doctrine.website.doctrine_users:
-
name: Symfony
url: https://symfony.com/

View File

@@ -77,25 +77,25 @@ parameters:
-
repositoryName: DoctrineBundle
isIntegration: true
integration: true
integrationFor: orm
integrationType: symfony
-
repositoryName: DoctrineCacheBundle
isIntegration: true
integration: true
integrationFor: cache
integrationType: symfony
-
repositoryName: DoctrineMigrationsBundle
isIntegration: true
integration: true
integrationFor: migrations
integrationType: symfony
-
repositoryName: DoctrineMongoDBBundle
slug: doctrine-mongodb-bundle
isIntegration: true
integration: true
integrationFor: mongodb-odm
integrationType: symfony

View File

@@ -13,7 +13,7 @@
<bind key="$projectsData">%doctrine.website.projects_data%</bind>
<bind key="$teamMembers">%doctrine.website.team_members%</bind>
<bind key="$templatesPath">%doctrine.website.templates_path%</bind>
<bind key="$whoUsesDoctrine">%doctrine.website.who_uses_doctrine%</bind>
<bind key="$doctrineUsers">%doctrine.website.doctrine_users%</bind>
<bind key="$projectIntegrationTypes">%doctrine.website.project_integration.types%</bind>
</defaults>
@@ -21,7 +21,7 @@
<service id="Doctrine\Website\Application" autowire="true" public="true" />
<service id="Doctrine\Website\Projects\ProjectRepository" autowire="true" public="true" />
<service id="Doctrine\Website\Projects\ProjectDataRepository" autowire="true" />
<service id="Doctrine\RST\Builder">
<argument type="service" id="Doctrine\Website\RST\Kernel" />
@@ -33,10 +33,27 @@
</service>
<service id="Doctrine\RST\HTML\Kernel" autowire="true" />
<service id="Github\Client">
<call method="authenticate">
<argument>%doctrine.website.github.http_token%</argument>
<argument></argument>
<argument>http_token</argument>
</call>
<call method="addCache">
<argument type="service">
<service class="Cache\Adapter\Doctrine\DoctrineCachePool" autowire="false">
<argument type="service" id="Doctrine\Common\Cache\FilesystemCache" />
</service>
</argument>
</call>
</service>
<service id="Highlight\Highlighter" autowire="true" />
<service id="Parsedown" class="Parsedown" autowire="true" />
<service id="Symfony\Component\Console\Application" autowire="true" />
<service id="Symfony\Component\Filesystem\Filesystem" autowire="true" />
<service id="Doctrine\Common\Cache\FilesystemCache" autowire="false">
<argument>%doctrine.website.cache_dir%</argument>
</service>
<service id="Doctrine\Website\Controller\ControllerProvider">
<argument type="collection">
@@ -86,5 +103,196 @@
<argument type="service" id="Doctrine\Website\RST\Directive\WarningDirective" />
</argument>
</service>
<service id="Doctrine\Common\EventManager" autowire="false" />
<service id="Doctrine\SkeletonMapper\Mapping\ClassMetadataInstantiator" autowire="false" />
<service id="Doctrine\SkeletonMapper\Mapping\ClassMetadataFactory" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\Mapping\ClassMetadataInstantiator" />
</service>
<service id="Doctrine\SkeletonMapper\ObjectFactory" autowire="false" />
<service id="Doctrine\SkeletonMapper\ObjectIdentityMap" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectRepository\ObjectRepositoryFactory" />
<argument type="service" id="Doctrine\SkeletonMapper\Mapping\ClassMetadataFactory" />
</service>
<service id="Doctrine\SkeletonMapper\ObjectManager" autowire="false" />
<service id="Doctrine\SkeletonMapper\Persister\ObjectPersisterFactory" autowire="false" />
<service id="Doctrine\Website\Repositories\BlogPostRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service">
<service class="Doctrine\Website\DataSource\DataSourceObjectDataRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service" id="Doctrine\Website\DataSources\BlogPosts" />
<argument>Doctrine\Website\Model\BlogPost</argument>
</service>
</argument>
<argument type="service" id="Doctrine\SkeletonMapper\ObjectFactory" />
<argument type="service">
<service class="Doctrine\SkeletonMapper\Hydrator\BasicObjectHydrator">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
</service>
</argument>
<argument type="service" id="Doctrine\Common\EventManager" />
<argument>Doctrine\Website\Model\BlogPost</argument>
</service>
<service id="Doctrine\Website\Repositories\ContributorRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service">
<service class="Doctrine\Website\DataSource\DataSourceObjectDataRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service" id="Doctrine\Website\DataSources\Contributors" />
<argument>Doctrine\Website\Model\Contributor</argument>
</service>
</argument>
<argument type="service" id="Doctrine\SkeletonMapper\ObjectFactory" />
<argument type="service">
<service class="Doctrine\SkeletonMapper\Hydrator\BasicObjectHydrator">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
</service>
</argument>
<argument type="service" id="Doctrine\Common\EventManager" />
<argument>Doctrine\Website\Model\Contributor</argument>
</service>
<service id="Doctrine\Website\Repositories\DoctrineUserRepository" autowire="false" public="true">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service">
<service class="Doctrine\Website\DataSource\DataSourceObjectDataRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service" id="Doctrine\Website\DataSources\DoctrineUsers" />
<argument>Doctrine\Website\Model\DoctrineUser</argument>
</service>
</argument>
<argument type="service" id="Doctrine\SkeletonMapper\ObjectFactory" />
<argument type="service">
<service class="Doctrine\SkeletonMapper\Hydrator\BasicObjectHydrator">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
</service>
</argument>
<argument type="service" id="Doctrine\Common\EventManager" />
<argument>Doctrine\Website\Model\DoctrineUser</argument>
</service>
<service id="Doctrine\Website\Repositories\ProjectRepository" autowire="false" public="true">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service">
<service class="Doctrine\Website\DataSource\DataSourceObjectDataRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service" id="Doctrine\Website\DataSources\Projects" />
<argument>Doctrine\Website\Model\Project</argument>
</service>
</argument>
<argument type="service" id="Doctrine\SkeletonMapper\ObjectFactory" />
<argument type="service">
<service class="Doctrine\SkeletonMapper\Hydrator\BasicObjectHydrator">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
</service>
</argument>
<argument type="service" id="Doctrine\Common\EventManager" />
<argument>Doctrine\Website\Model\Project</argument>
</service>
<service id="Doctrine\Website\Repositories\ProjectContributorRepository" autowire="false" public="true">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service">
<service class="Doctrine\Website\DataSource\DataSourceObjectDataRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service" id="Doctrine\Website\DataSources\ProjectContributors" />
<argument>Doctrine\Website\Model\ProjectContributor</argument>
</service>
</argument>
<argument type="service" id="Doctrine\SkeletonMapper\ObjectFactory" />
<argument type="service">
<service class="Doctrine\SkeletonMapper\Hydrator\BasicObjectHydrator">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
</service>
</argument>
<argument type="service" id="Doctrine\Common\EventManager" />
<argument>Doctrine\Website\Model\ProjectContributor</argument>
</service>
<service id="Doctrine\Website\Repositories\SitemapPageRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service">
<service class="Doctrine\Website\DataSource\DataSourceObjectDataRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service" id="Doctrine\Website\DataSources\SitemapPages" />
<argument>Doctrine\Website\Model\SitemapPage</argument>
</service>
</argument>
<argument type="service" id="Doctrine\SkeletonMapper\ObjectFactory" />
<argument type="service">
<service class="Doctrine\SkeletonMapper\Hydrator\BasicObjectHydrator">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
</service>
</argument>
<argument type="service" id="Doctrine\Common\EventManager" />
<argument>Doctrine\Website\Model\SitemapPage</argument>
</service>
<service id="Doctrine\Website\Repositories\TeamMemberRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service">
<service class="Doctrine\Website\DataSource\DataSourceObjectDataRepository" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
<argument type="service" id="Doctrine\Website\DataSources\TeamMembers" />
<argument>Doctrine\Website\Model\TeamMember</argument>
</service>
</argument>
<argument type="service" id="Doctrine\SkeletonMapper\ObjectFactory" />
<argument type="service">
<service class="Doctrine\SkeletonMapper\Hydrator\BasicObjectHydrator">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectManager" />
</service>
</argument>
<argument type="service" id="Doctrine\Common\EventManager" />
<argument>Doctrine\Website\Model\TeamMember</argument>
</service>
<service id="Doctrine\SkeletonMapper\ObjectRepository\ObjectRepositoryFactory" autowire="false">
<call method="addObjectRepository">
<argument>Doctrine\Website\Model\BlogPost</argument>
<argument type="service" id="Doctrine\Website\Repositories\BlogPostRepository" />
</call>
<call method="addObjectRepository">
<argument>Doctrine\Website\Model\Contributor</argument>
<argument type="service" id="Doctrine\Website\Repositories\ContributorRepository" />
</call>
<call method="addObjectRepository">
<argument>Doctrine\Website\Model\DoctrineUser</argument>
<argument type="service" id="Doctrine\Website\Repositories\DoctrineUserRepository" />
</call>
<call method="addObjectRepository">
<argument>Doctrine\Website\Model\Project</argument>
<argument type="service" id="Doctrine\Website\Repositories\ProjectRepository" />
</call>
<call method="addObjectRepository">
<argument>Doctrine\Website\Model\ProjectContributor</argument>
<argument type="service" id="Doctrine\Website\Repositories\ProjectContributorRepository" />
</call>
<call method="addObjectRepository">
<argument>Doctrine\Website\Model\TeamMember</argument>
<argument type="service" id="Doctrine\Website\Repositories\TeamMemberRepository" />
</call>
<call method="addObjectRepository">
<argument>Doctrine\Website\Model\SitemapPage</argument>
<argument type="service" id="Doctrine\Website\Repositories\SitemapPageRepository" />
</call>
</service>
<service id="Doctrine\SkeletonMapper\ObjectManager" autowire="false">
<argument type="service" id="Doctrine\SkeletonMapper\ObjectRepository\ObjectRepositoryFactory" />
<argument type="service" id="Doctrine\SkeletonMapper\Persister\ObjectPersisterFactory" />
<argument type="service" id="Doctrine\SkeletonMapper\ObjectIdentityMap" />
<argument type="service" id="Doctrine\SkeletonMapper\Mapping\ClassMetadataFactory" />
<argument type="service" id="Doctrine\Common\EventManager" />
</service>
</services>
</container>

View File

@@ -1,32 +1,19 @@
parameters:
doctrine.website.team_members:
# active
alcaeus:
name: Andreas
github: alcaeus
avatarUrl: 'https://avatars2.githubusercontent.com/u/383198?v=4'
website: ~
location: Munich
active: true
core: true
projects: ["mongodb-odm", "mongodb"]
maintains: ["mongodb-odm", "mongodb", "doctrine-mongodb-bundle"]
beberlei:
name: 'Benjamin Eberlei'
github: beberlei
avatarUrl: 'https://avatars2.githubusercontent.com/u/26936?v=4'
website: 'https://beberlei.de'
location: Germany
active: true
core: true
projects: ["orm", "dbal", "couchdb-odm"]
billschaller:
name: 'Bill Schaller'
github: billschaller
avatarUrl: 'https://avatars1.githubusercontent.com/u/4266849?v=4'
website: ~
location: 'New York, NY'
active: true
core: true
maintains: ["orm", "dbal", "common", "cache", "annotations", "collections", "event-manager"]
carusogabriel:
name: 'Gabriel Caruso'
twitter: carusogabriel
@@ -34,18 +21,14 @@ parameters:
avatarUrl: 'https://avatars3.githubusercontent.com/u/16328050?v=4'
website: ~
location: Brazil
active: true
core: true
projects: ["coding-standard"]
maintains: ["coding-standard"]
deeky666:
name: 'Steve Müller'
github: deeky666
avatarUrl: 'https://avatars2.githubusercontent.com/u/1411514?v=4'
website: 'http://dzh-online.de/'
location: 'Hamburg, Germany'
active: true
core: true
projects: ["dbal", "coding-standard"]
maintains: ["dbal", "coding-standard"]
greg0ire:
name: 'Grégoire Paris'
twitter: greg0ire
@@ -53,9 +36,7 @@ parameters:
avatarUrl: 'https://avatars1.githubusercontent.com/u/657779?v=4'
website: 'https://greg0ire.fr'
location: Paris
active: true
core: true
projects: ["dbal", "orm", "coding-standard"]
maintains: ["dbal", "orm", "coding-standard"]
guilhermeblanco:
name: 'Guilherme Blanco'
twitter: guilhermeblanco
@@ -63,9 +44,7 @@ parameters:
avatarUrl: 'https://avatars1.githubusercontent.com/u/208883?v=4'
website: 'http://www.doctrine-project.org'
location: 'Toronto, ON, Canada'
active: true
core: true
projects: ["orm", "dbal", "annotations", "doctrine1"]
maintains: ["orm", "dbal", "annotations", "doctrine1", "common", "cache", "event-manager", "lexer", "doctrine-cache-bundle"]
jmikola:
name: 'Jeremy Mikola'
twitter: jmikola
@@ -73,9 +52,7 @@ parameters:
avatarUrl: 'https://avatars3.githubusercontent.com/u/244663?v=4'
website: 'http://jmikola.net'
location: 'Hoboken, NJ'
active: true
core: true
projects: ["mongodb", "mongodb-odm"]
maintains: ["mongodb", "mongodb-odm", "doctrine-mongodb-bundle"]
jwage:
name: 'Jonathan H. Wage'
twitter: jwage
@@ -83,10 +60,7 @@ parameters:
github: jwage
avatarUrl: 'https://avatars0.githubusercontent.com/u/97422?v=4'
location: 'Nashville, TN'
active: true
core: true
documentation: true
projects: ["mongodb", "mongodb-odm", "migrations", "doctrine1", "event-manager", "persistence", "reflection", "rst-parser", "skeleton-mapper"]
maintains: ["mongodb", "mongodb-odm", "migrations", "doctrine1", "event-manager", "persistence", "reflection", "rst-parser", "skeleton-mapper", "common", "doctrine-migrations-bundle", "inflector", "lexer"]
kimhemsoe:
name: 'Kim Hemsø Rasmussen'
twitter: kimhemsoe
@@ -94,8 +68,7 @@ parameters:
avatarUrl: 'https://avatars3.githubusercontent.com/u/568787?v=4'
website: ~
location: Copenhagen
active: true
core: true
maintains: ["doctrine-bundle"]
lcobucci:
name: 'Luís Cobucci'
twitter: lcobucci
@@ -103,9 +76,7 @@ parameters:
avatarUrl: 'https://avatars2.githubusercontent.com/u/201963?v=4'
website: ~
location: 'Amsterdam, NL'
active: true
core: true
projects: ["orm", "dbal", "migrations", "cache", "coding-standard"]
maintains: ["orm", "dbal", "migrations", "cache", "coding-standard", "collections"]
Majkl578:
name: 'Michael Moravec'
twitter: Majkl578
@@ -113,9 +84,7 @@ parameters:
avatarUrl: 'https://avatars2.githubusercontent.com/u/144181?v=4'
website: 'https://majkl.me'
location: 'Prague, Czech Republic'
active: true
core: true
projects: ["orm", "dbal", "event-manager", "annotations", "persistence", "reflection", "coding-standard"]
maintains: ["orm", "dbal", "event-manager", "annotations", "persistence", "reflection", "coding-standard", "common"]
mikeSimonson:
name: "Mike Simonson"
twitter: _mikeSimonson
@@ -123,9 +92,7 @@ parameters:
avatarUrl: 'https://avatars3.githubusercontent.com/u/907613?v=4'
website: ~
location: ~
active: true
core: true
projects: ["migrations"]
maintains: ["migrations"]
malarzm:
name: 'Maciej Malarz'
twitter: malarzm
@@ -133,9 +100,7 @@ parameters:
avatarUrl: 'https://avatars1.githubusercontent.com/u/4947711?v=4'
website: ~
location: Cracow
active: true
core: true
projects: ["mongodb", "mongodb-odm"]
maintains: ["mongodb", "mongodb-odm", "doctrine-mongodb-bundle"]
morozov:
name: 'Sergei Morozov'
twitter: srgmrzv
@@ -143,9 +108,7 @@ parameters:
avatarUrl: 'https://avatars1.githubusercontent.com/u/59683?v=4'
website: 'http://morozov.livejournal.com/'
location: 'San Jose, CA'
active: true
core: true
projects: ["dbal"]
maintains: ["dbal"]
Ocramius:
name: 'Marco Pivetta'
twitter: ocramius
@@ -153,243 +116,31 @@ parameters:
github: Ocramius
avatarUrl: 'https://avatars2.githubusercontent.com/u/154256?v=4'
location: 'Frankfurt am Main'
active: true
core: true
projects: ["orm", "dbal", "reflection", "instantiator", "coding-standard"]
maintains: ["orm", "dbal", "reflection", "instantiator", "coding-standard", "common", "cache", "annotations", "collections", "event-manager", "lexer"]
TomHAnderson:
name: 'Tom H Anderson'
github: TomHAnderson
avatarUrl: 'https://avatars1.githubusercontent.com/u/493920?v=4'
website: 'http://tomhanderson.com'
location: 'Salt Lake City, Utah'
active: true
core: true
maintains: ["doctrine-module"]
dbu:
name: 'David Buchmann'
github: dbu
avatarUrl: 'https://avatars3.githubusercontent.com/u/76576?v=4'
website: 'http://davidbu.ch'
location: Switzerland
active: true
core: true
projects: ["phpcr-odm"]
maintains: ["phpcr-odm"]
weaverryan:
name: 'Ryan Weaver'
github: weaverryan
avatarUrl: 'https://avatars1.githubusercontent.com/u/121003?v=4'
website: 'http://knpuniversity.com'
location: 'Grand Rapids MI'
active: true
documentation: true
projects: ["rst-parser"]
maintains: ["rst-parser", "doctrine-bundle"]
SenseException:
name: 'Claudio Zizza'
github: 'SenseException'
avatarUrl: 'https://avatars3.githubusercontent.com/u/859964?v=4'
website: 'http://php.budgegeria.de/'
location: 'Karlsruhe'
active: true
documentation: true
# inactive
zYne-:
name: Konsta Vesterinen
github: kvesteri
avatarUrl: https://avatars3.githubusercontent.com/u/398714?v=4
website: 'http://www.fastmonkeys.com/'
location: Espoo, Finland
active: false
founder: true
projects: ["doctrine1"]
romanb:
name: Roman Borschel
github: romanb
avatarUrl: https://avatars1.githubusercontent.com/u/177309?v=4
website: ~
location: Berlin, Germany
active: false
projects: ["doctrine1", "orm", "dbal", "common", "annotations", "lexer", "collections", "cache", "inflector"]
asm89:
name: Alexander
github: asm89
avatarUrl: 'https://avatars0.githubusercontent.com/u/657357?v=4'
website: ~
location: ~
avalanche123:
name: 'Bulat Shakirzyanov'
github: avalanche123
avatarUrl: 'https://avatars0.githubusercontent.com/u/83289?v=4'
website: 'http://avalanche123.com/'
location: 'Conshohocken, PA'
Baachi:
name: 'Markus Bachmann'
github: Baachi
avatarUrl: 'https://avatars1.githubusercontent.com/u/833645?v=4'
website: 'http://www.bachi.biz'
location: Germany
bakura10:
name: 'Michaël Gallego'
github: bakura10
avatarUrl: 'https://avatars1.githubusercontent.com/u/1198915?v=4'
website: 'http://www.michaelgallego.fr'
location: 'Paris, France'
dantleech:
name: dantleech
github: dantleech
avatarUrl: 'https://avatars1.githubusercontent.com/u/530801?v=4'
website: 'http://www.dantleech.com'
location: Europe
projects: ["phpcr-odm"]
davidino:
name: 'David Funaro'
github: davidino
avatarUrl: 'https://avatars2.githubusercontent.com/u/265253?v=4'
website: 'http://www.davidfunaro.com'
location: Dubai
ElectricMaxxx:
name: 'Maximilian Berghoff'
github: ElectricMaxxx
avatarUrl: 'https://avatars1.githubusercontent.com/u/2905834?v=4'
website: ~
location: 'Ansbach, Germany'
EmanueleMinotto:
name: 'Emanuele Minotto'
github: EmanueleMinotto
avatarUrl: 'https://avatars2.githubusercontent.com/u/417201?v=4'
website: 'http://emanueleminotto.github.io'
location: ~
FabioBatSilva:
name: 'Fabio B. Silva'
twitter: FabioBatSilva
github: FabioBatSilva
avatarUrl: 'https://avatars2.githubusercontent.com/u/588172?v=4'
website: ~
location: 'Toronto, Canada'
fabpot:
name: 'Fabien Potencier'
github: fabpot
avatarUrl: 'https://avatars3.githubusercontent.com/u/47313?v=4'
website: 'http://fabien.potencier.org/'
location: 'San Francisco'
gianarb:
name: 'Gianluca Arbezzano'
github: gianarb
avatarUrl: 'https://avatars1.githubusercontent.com/u/1630267?v=4'
website: 'https://gianarb.it'
location: ~
igorgolovanov:
name: 'Igor Golovanov'
github: igorgolovanov
avatarUrl: 'https://avatars3.githubusercontent.com/u/363810?v=4'
website: 'https://www.linkedin.com/in/igorgolovanov'
location: ~
igorw:
name: Igor
github: igorw
avatarUrl: 'https://avatars2.githubusercontent.com/u/88061?v=4'
website: ~
location: ~
kore:
name: 'Kore Nordmann'
github: kore
avatarUrl: 'https://avatars0.githubusercontent.com/u/154398?v=4'
website: 'http://kore-nordmann.de'
location: ~
kriswallsmith:
name: 'Kris Wallsmith'
github: kriswallsmith
avatarUrl: 'https://avatars2.githubusercontent.com/u/33886?v=4'
website: 'http://kriswallsmith.net'
location: 'Portland, Oregon USA'
lavoiesl:
name: 'Sébastien Lavoie'
github: lavoiesl
avatarUrl: 'https://avatars0.githubusercontent.com/u/1216046?v=4'
website: 'http://blog.lavoie.sl'
location: 'California, US'
lsmith77:
name: 'Lukas Kahwe Smith'
github: lsmith77
avatarUrl: 'https://avatars0.githubusercontent.com/u/300279?v=4'
website: 'http://pooteeweet.org'
location: 'Zurich, Switzerland'
projects: ["couchdb-odm", "phpcr-odm"]
mikelohmann:
name: 'Mike Lohmann'
github: mikelohmann
avatarUrl: 'https://avatars0.githubusercontent.com/u/600423?v=4'
website: 'http://elbstack.com'
location: Hamburg
MrHash:
name: 'Hasham Ahmad'
github: MrHash
avatarUrl: 'https://avatars3.githubusercontent.com/u/390925?v=4'
website: ~
location: 'Berlin, London, Tokyo, and more'
naderman:
name: 'Nils Adermann'
github: naderman
avatarUrl: 'https://avatars1.githubusercontent.com/u/154844?v=4'
website: 'http://www.naderman.de'
location: 'Berlin, Germany'
nicam:
name: 'Pascal Helfenstein'
github: nicam
avatarUrl: 'https://avatars0.githubusercontent.com/u/182071?v=4'
website: 'https://nicam.ch/'
location: 'Zürich Switzerland'
nrk:
name: 'Daniele Alessandri'
github: nrk
avatarUrl: 'https://avatars0.githubusercontent.com/u/17923?v=4'
website: 'http://clorophilla.net/'
location: 'Milan, Italy'
odino:
name: 'Alessandro Nadalin'
github: odino
avatarUrl: 'https://avatars3.githubusercontent.com/u/328420?v=4'
website: 'https://odino.org/'
location: 'Dubai, UAE'
projects: [orientdb-odm]
richardfullmer:
name: 'Richard Fullmer'
github: richardfullmer
avatarUrl: 'https://avatars0.githubusercontent.com/u/384602?v=4'
website: ~
location: 'Las Vegas'
rndstr:
name: 'Roland Schilter'
github: rndstr
avatarUrl: 'https://avatars0.githubusercontent.com/u/32963?v=4'
website: ~
location: 'San Francisco'
schmittjoh:
name: Johannes
github: schmittjoh
avatarUrl: 'https://avatars2.githubusercontent.com/u/197017?v=4'
website: 'https://scrutinizer-ci.com'
location: ~
Seldaek:
name: 'Jordi Boggiano'
github: Seldaek
avatarUrl: 'https://avatars1.githubusercontent.com/u/183678?v=4'
website: 'https://seld.be/'
location: 'Zürich, Zurich, Switzerland'
sixty-nine:
name: Dan
github: sixty-nine
avatarUrl: 'https://avatars1.githubusercontent.com/u/398679?v=4'
website: ~
location: ~
stof:
name: 'Christophe Coevoet'
github: stof
avatarUrl: 'https://avatars0.githubusercontent.com/u/439401?v=4'
website: ~
location: Paris
superdweebie:
name: 'Tim Roediger'
github: superdweebie
avatarUrl: 'https://avatars1.githubusercontent.com/u/1670364?v=4'
website: ~
location: 'Sydney, Australia'

View File

@@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use function file_exists;
use function getenv;
use function realpath;
use function sprintf;
@@ -65,6 +66,8 @@ class Application
$container->setParameter('doctrine.website.env', $env);
$container->setParameter('doctrine.website.root_dir', realpath(__DIR__ . '/..'));
$container->setParameter('doctrine.website.config_dir', realpath(__DIR__ . '/../config'));
$container->setParameter('doctrine.website.cache_dir', realpath(__DIR__ . '/../cache'));
$container->setParameter('doctrine.website.github.http_token', getenv('doctrine_website_github_http_token'));
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
$loader->load('services.xml');

View File

@@ -1,68 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Blog;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Builder\SourceFileRepository;
use InvalidArgumentException;
use function array_map;
use function array_reverse;
use function array_slice;
use function usort;
class BlogPostRepository
{
/** @var SourceFileRepository */
private $sourceFileRepository;
public function __construct(SourceFileRepository $sourceFileRepository)
{
$this->sourceFileRepository = $sourceFileRepository;
}
public function find(SourceFile $sourceFile) : BlogPost
{
return new BlogPost(
$sourceFile->getParameter('url'),
$sourceFile->getParameter('title'),
(string) $sourceFile->getParameter('authorName'),
(string) $sourceFile->getParameter('authorEmail'),
$sourceFile->getContents(),
$sourceFile->getDate()
);
}
/**
* @return BlogPost[]
*/
public function findPaginated(int $page = 1, int $perPage = 10) : array
{
if ($page < 1 || $perPage < 1) {
throw new InvalidArgumentException('Pagination parameters must be positive.');
}
$offset = ($page - 1) * $perPage;
return array_slice($this->findAll(), $offset, $perPage);
}
/**
* @return BlogPost[]
*/
public function findAll() : array
{
$sourceFiles = $this->sourceFileRepository->getFiles('', 'source/blog');
usort($sourceFiles, static function (SourceFile $a, SourceFile $b) : int {
return $a->getDate()->getTimestamp() - $b->getDate()->getTimestamp();
});
$reversedSourceFiles = array_reverse($sourceFiles);
return array_map(function (SourceFile $sourceFile) : BlogPost {
return $this->find($sourceFile);
}, $reversedSourceFiles);
}
}

View File

@@ -4,12 +4,14 @@ declare(strict_types=1);
namespace Doctrine\Website\Commands;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Repositories\ProjectRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use function array_filter;
use function glob;
use function sprintf;
class ClearBuildCacheCommand extends Command
@@ -88,6 +90,12 @@ class ClearBuildCacheCommand extends Command
);
}
$cacheDirectories = array_filter(glob($this->rootDir . '/cache/*'), 'is_dir');
foreach ($cacheDirectories as $cacheDirectory) {
$remove[] = $cacheDirectory;
}
foreach ($remove as $path) {
$output->writeln(sprintf('Removing <info>%s</info>', $path));

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\Website\Controllers;
use Doctrine\Website\Blog\BlogPostRepository;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Controller\ControllerResult;
use Doctrine\Website\Repositories\BlogPostRepository;
class AtomController
{

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\Website\Controllers;
use Doctrine\Website\Blog\BlogPostRepository;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Controller\ControllerResult;
use Doctrine\Website\Repositories\BlogPostRepository;
class BlogController
{
@@ -35,7 +35,7 @@ class BlogController
public function view(SourceFile $sourceFile) : ControllerResult
{
return new ControllerResult([
'blogPost' => $this->blogPostRepository->find($sourceFile),
'blogPost' => $this->blogPostRepository->find($sourceFile->getParameter('url')),
]);
}
}

View File

@@ -6,7 +6,7 @@ namespace Doctrine\Website\Controllers;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Controller\ControllerResult;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Repositories\ProjectRepository;
class DocumentationController
{
@@ -20,7 +20,7 @@ class DocumentationController
public function view(SourceFile $sourceFile) : ControllerResult
{
$project = $this->projectRepository->findOneBySlug($sourceFile->getParameter('docsSlug'));
$project = $this->projectRepository->findOneByDocsSlug($sourceFile->getParameter('docsSlug'));
return new ControllerResult([
'project' => $project,

View File

@@ -4,10 +4,11 @@ declare(strict_types=1);
namespace Doctrine\Website\Controllers;
use Doctrine\Website\Blog\BlogPostRepository;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Controller\ControllerResult;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Repositories\BlogPostRepository;
use Doctrine\Website\Repositories\DoctrineUserRepository;
use Doctrine\Website\Repositories\ProjectRepository;
class HomepageController
{
@@ -17,31 +18,29 @@ class HomepageController
/** @var ProjectRepository */
private $projectRepository;
/** @var string[][] */
private $whoUsesDoctrine;
/** @var DoctrineUserRepository */
private $doctrineUserRepository;
/**
* @param string[][] $whoUsesDoctrine
*/
public function __construct(
BlogPostRepository $blogPostRepository,
ProjectRepository $projectRepository,
array $whoUsesDoctrine
DoctrineUserRepository $doctrineUserRepository
) {
$this->blogPostRepository = $blogPostRepository;
$this->projectRepository = $projectRepository;
$this->whoUsesDoctrine = $whoUsesDoctrine;
$this->blogPostRepository = $blogPostRepository;
$this->projectRepository = $projectRepository;
$this->doctrineUserRepository = $doctrineUserRepository;
}
public function index(SourceFile $sourceFile) : ControllerResult
{
$blogPosts = $this->blogPostRepository->findPaginated(1, 10);
$primaryProjects = $this->projectRepository->findPrimaryProjects();
$doctrineUsers = $this->doctrineUserRepository->findAll();
return new ControllerResult([
'blogPosts' => $blogPosts,
'primaryProjects' => $primaryProjects,
'whoUsesDoctrine' => $this->whoUsesDoctrine,
'doctrineUsers' => $doctrineUsers,
]);
}
}

View File

@@ -6,21 +6,23 @@ namespace Doctrine\Website\Controllers;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Controller\ControllerResult;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Team\TeamRepository;
use Doctrine\Website\Repositories\ProjectContributorRepository;
use Doctrine\Website\Repositories\ProjectRepository;
class ProjectController
{
/** @var ProjectRepository */
private $projectRepository;
/** @var TeamRepository */
private $teamRepository;
/** @var ProjectContributorRepository */
private $projectContributorRepository;
public function __construct(ProjectRepository $projectRepository, TeamRepository $teamRepository)
{
$this->projectRepository = $projectRepository;
$this->teamRepository = $teamRepository;
public function __construct(
ProjectRepository $projectRepository,
ProjectContributorRepository $projectContributorRepository
) {
$this->projectRepository = $projectRepository;
$this->projectContributorRepository = $projectContributorRepository;
}
public function index(SourceFile $sourceFile) : ControllerResult
@@ -35,14 +37,13 @@ class ProjectController
public function view(SourceFile $sourceFile) : ControllerResult
{
$project = $this->projectRepository->findOneBySlug($sourceFile->getParameter('docsSlug'));
$project = $this->projectRepository->findOneByDocsSlug($sourceFile->getParameter('docsSlug'));
return new ControllerResult([
'project' => $project,
'allTeamMembers' => $this->teamRepository->getAllProjectTeamMembers($project),
'activeTeamMembers' => $this->teamRepository->getActiveProjectTeamMembers($project),
'inactiveTeamMembers' => $this->teamRepository->getInactiveProjectTeamMembers($project),
'integrationProjects' => $this->projectRepository->findProjectIntegrations($project),
'maintainers' => $this->projectContributorRepository->findMaintainersByProject($project),
'contributors' => $this->projectContributorRepository->findContributorsByProject($project),
]);
}
}

View File

@@ -6,7 +6,7 @@ namespace Doctrine\Website\Controllers;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Controller\ControllerResult;
use Doctrine\Website\Sitemap\SitemapPageRepository;
use Doctrine\Website\Repositories\SitemapPageRepository;
class SitemapController
{

View File

@@ -6,24 +6,29 @@ namespace Doctrine\Website\Controllers;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Controller\ControllerResult;
use Doctrine\Website\Team\TeamRepository;
use Doctrine\Website\Repositories\ContributorRepository;
class TeamController
{
/** @var TeamRepository */
private $teamRepository;
/** @var ContributorRepository */
private $contributorRepository;
public function __construct(TeamRepository $teamRepository)
public function __construct(ContributorRepository $contributorRepository)
{
$this->teamRepository = $teamRepository;
$this->contributorRepository = $contributorRepository;
}
public function index(SourceFile $sourceFile) : ControllerResult
public function maintainers(SourceFile $sourceFile) : ControllerResult
{
return new ControllerResult([
'activeCoreTeamMembers' => $this->teamRepository->getActiveCoreTeamMembers(),
'activeDocumentationTeamMembers' => $this->teamRepository->getActiveDocumentationTeamMembers(),
'inactiveTeamMembers' => $this->teamRepository->getInactiveTeamMembers(),
'contributors' => $this->contributorRepository->findMaintainers(),
]);
}
public function contributors(SourceFile $sourceFile) : ControllerResult
{
return new ControllerResult([
'contributors' => $this->contributorRepository->findContributors(),
]);
}
}

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSource;
use function in_array;
class CriteriaMatcher
{
/** @var mixed[] */
private $criteria;
/** @var mixed[] */
private $row;
/**
* @param mixed[] $criteria
* @param mixed[] $row
*/
public function __construct(array $criteria, array $row)
{
$this->criteria = $criteria;
$this->row = $row;
}
public function matches() : bool
{
$matches = true;
foreach ($this->criteria as $key => $value) {
if ($this->criteriaElementMatches($key, $value)) {
continue;
}
$matches = false;
}
return $matches;
}
/**
* @param mixed $value
*/
private function criteriaElementMatches(string $key, $value) : bool
{
if (isset($value['$contains'])) {
if ($this->contains($key, $value)) {
return true;
}
} elseif ($this->equals($key, $value)) {
return true;
}
return false;
}
/**
* @param mixed[] $value
*/
private function contains(string $key, array $value) : bool
{
return isset($this->row[$key]) && in_array($value['$contains'], $this->row[$key], true);
}
/**
* @param mixed $value
*/
private function equals(string $key, $value) : bool
{
return isset($this->row[$key]) && $this->row[$key] === $value;
}
}

View File

@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSource;
interface DataSource
{
/**
* @return mixed[][]
*/
public function getSourceRows() : array;
}

View File

@@ -0,0 +1,139 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSource;
use Doctrine\SkeletonMapper\DataRepository\BasicObjectDataRepository;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
use function array_slice;
use function count;
use function usort;
class DataSourceObjectDataRepository extends BasicObjectDataRepository
{
/** @var DataSource */
private $dataSource;
/** @var mixed[][]|null */
private $sourceRows;
public function __construct(
ObjectManagerInterface $objectManager,
DataSource $dataSource,
string $className
) {
parent::__construct($objectManager, $className);
$this->dataSource = $dataSource;
}
/**
* @return mixed[][]
*/
public function findAll() : array
{
return $this->getSourceRows();
}
/**
* @param mixed[] $criteria
* @param mixed[] $orderBy
*
* @return mixed[][]
*/
public function findBy(
array $criteria,
?array $orderBy = null,
?int $limit = null,
?int $offset = null
) : array {
$rows = [];
foreach ($this->getSourceRows() as $row) {
if (! $this->matches($criteria, $row)) {
continue;
}
$rows[] = $row;
}
if ($orderBy !== null && $orderBy !== []) {
$rows = $this->sort($rows, $orderBy);
}
if ($limit !== null || $offset !== null) {
return $this->slice($rows, $limit, $offset);
}
return $rows;
}
/**
* @param mixed[] $criteria
*
* @return mixed[]|null
*/
public function findOneBy(array $criteria) : ?array
{
foreach ($this->getSourceRows() as $row) {
if ($this->matches($criteria, $row)) {
return $row;
}
}
return null;
}
/**
* @param mixed[] $criteria
* @param mixed[] $row
*/
private function matches(array $criteria, array $row) : bool
{
return (new CriteriaMatcher($criteria, $row))->matches();
}
/**
* @param mixed[][] $rows
* @param string[] $orderBy
*
* @return mixed[][] $rows
*/
private function sort(array $rows, array $orderBy) : array
{
usort($rows, new Sorter($orderBy));
return $rows;
}
/**
* @param mixed[][] $rows
*
* @return mixed[][] $rows
*/
private function slice(array $rows, ?int $limit, ?int $offset) : array
{
if ($limit === null) {
$limit = count($rows);
}
if ($offset === null) {
$offset = 0;
}
return array_slice($rows, $offset, $limit);
}
/**
* @return mixed[][]
*/
private function getSourceRows() : array
{
if ($this->sourceRows === null) {
$this->sourceRows = $this->dataSource->getSourceRows();
}
return $this->sourceRows;
}
}

106
lib/DataSource/Sorter.php Normal file
View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSource;
use InvalidArgumentException;
use function count;
use function is_string;
use function sprintf;
use function strtolower;
class Sorter
{
private const ORDER_ASC = 'asc';
private const ORDER_DESC = 'desc';
/** @var int */
private $level = 0;
/** @var string[] */
private $fields;
/** @var int[] */
private $orders;
/**
* @param string[] $orderBy
*/
public function __construct(array $orderBy)
{
if ($orderBy === []) {
throw new InvalidArgumentException('The Sorter class does not accept an empty $orderBy');
}
foreach ($orderBy as $field => $order) {
$this->fields[] = $field;
$this->orders[] = $this->getOrder($order);
}
}
/**
* @param mixed[] $a
* @param mixed[] $b
*/
public function __invoke(array $a, array $b) : int
{
$returnVal = 0;
$comparisonField = $this->fields[$this->level];
$order = $this->orders[$this->level];
$aComparisonField = $this->getComparisonField($a, $comparisonField);
$bComparisonField = $this->getComparisonField($b, $comparisonField);
$comparisonResult = $aComparisonField <=> $bComparisonField;
if ($comparisonResult !== 0) {
$returnVal = $comparisonResult;
} else {
if ($this->level < count($this->fields) - 1) {
$this->level++;
return $this->__invoke($a, $b);
}
}
$returnVal *= $order;
$this->level = 0;
return $returnVal;
}
private function getOrder(string $order) : int
{
$lowercaseOrder = strtolower($order);
if ($lowercaseOrder === self::ORDER_ASC) {
return 1;
}
if ($lowercaseOrder === self::ORDER_DESC) {
return -1;
}
throw new InvalidArgumentException(sprintf(
'$order value of %s is not accepted. Only a value of asc or desc is allowed.',
$order
));
}
/**
* @param mixed[] $item
*
* @return mixed
*/
private function getComparisonField(array $item, string $field)
{
if (! isset($item[$field])) {
throw new InvalidArgumentException(sprintf('Unable to find comparison field %s', $field));
}
$value = $item[$field];
return is_string($value) ? strtolower($value) : $value;
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSources;
use Doctrine\Website\Builder\SourceFileRepository;
use Doctrine\Website\DataSource\DataSource;
use function array_reverse;
class BlogPosts implements DataSource
{
/** @var SourceFileRepository */
private $sourceFileRepository;
public function __construct(SourceFileRepository $sourceFileRepository)
{
$this->sourceFileRepository = $sourceFileRepository;
}
/**
* @return mixed[][]
*/
public function getSourceRows() : array
{
$sourceFiles = $this->sourceFileRepository->getFiles('', 'source/blog');
$reversedSourceFiles = array_reverse($sourceFiles);
$blogPostRows = [];
foreach ($reversedSourceFiles as $sourceFile) {
$blogPostRows[] = [
'url' => $sourceFile->getParameter('url'),
'title' => $sourceFile->getParameter('title'),
'authorName' => (string) $sourceFile->getParameter('authorName'),
'authorEmail' => (string) $sourceFile->getParameter('authorEmail'),
'contents' => $sourceFile->getContents(),
'date' => $sourceFile->getDate(),
];
}
return $blogPostRows;
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSources;
use Doctrine\Website\DataSource\DataSource;
use Doctrine\Website\Repositories\ProjectContributorRepository;
class Contributors implements DataSource
{
/** @var ProjectContributorRepository */
private $projectContributorRepository;
public function __construct(ProjectContributorRepository $projectContributorRepository)
{
$this->projectContributorRepository = $projectContributorRepository;
}
/**
* @return mixed[][]
*/
public function getSourceRows() : array
{
$projectContributors = $this->projectContributorRepository->findAll();
$contributorRows = [];
foreach ($projectContributors as $projectContributor) {
$github = $projectContributor->getGithub();
if (! isset($contributorRows[$github])) {
$contributorRows[$github] = [
'teamMember' => $projectContributor->getTeamMember(),
'isTeamMember' => $projectContributor->isTeamMember(),
'github' => $github,
'avatarUrl' => $projectContributor->getAvatarUrl(),
'numCommits' => 0,
'numAdditions' => 0,
'numDeletions' => 0,
'projects' => [],
];
}
$contributorRows[$github]['numCommits'] += $projectContributor->getNumCommits();
$contributorRows[$github]['numAdditions'] += $projectContributor->getNumAdditions();
$contributorRows[$github]['numDeletions'] += $projectContributor->getNumDeletions();
$contributorRows[$github]['projects'][] = $projectContributor->getProject();
}
return $contributorRows;
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSources;
use Doctrine\Website\DataSource\DataSource;
class DoctrineUsers implements DataSource
{
/** @var mixed[][] */
private $doctrineUsers;
/**
* @param mixed[][] $doctrineUsers
*/
public function __construct(array $doctrineUsers)
{
$this->doctrineUsers = $doctrineUsers;
}
/**
* @return mixed[][]
*/
public function getSourceRows() : array
{
return $this->doctrineUsers;
}
}

View File

@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSources;
use Doctrine\Website\DataSource\DataSource;
use Doctrine\Website\Github\GithubProjectContributors;
use Doctrine\Website\Repositories\ProjectRepository;
use Doctrine\Website\Repositories\TeamMemberRepository;
class ProjectContributors implements DataSource
{
/** @var ProjectRepository */
private $projectRepository;
/** @var TeamMemberRepository */
private $teamMemberRepository;
/** @var GithubProjectContributors */
private $githubProjectContributors;
public function __construct(
ProjectRepository $projectRepository,
TeamMemberRepository $teamMemberRepository,
GithubProjectContributors $githubProjectContributors
) {
$this->projectRepository = $projectRepository;
$this->teamMemberRepository = $teamMemberRepository;
$this->githubProjectContributors = $githubProjectContributors;
}
/**
* @return mixed[][]
*/
public function getSourceRows() : array
{
$projects = $this->projectRepository->findAll();
$projectContributorRows = [];
foreach ($projects as $project) {
$contributors = $this->githubProjectContributors->getProjectContributors($project);
foreach ($contributors as $contributor) {
$numAdditions = 0;
$numDeletions = 0;
foreach ($contributor['weeks'] as $week) {
$numAdditions += $week['a'];
$numDeletions += $week['d'];
}
$teamMember = $this->teamMemberRepository->findOneByGithub($contributor['author']['login']);
$isMaintainer = $teamMember !== null
? $teamMember->isProjectMaintainer($project)
: false;
$isTeamMember = $teamMember !== null;
$projectContributorRows[] = [
'teamMember' => $teamMember,
'isTeamMember' => $isTeamMember,
'isMaintainer' => $isMaintainer,
'projectSlug' => $project->getSlug(),
'project' => $project,
'github' => $contributor['author']['login'],
'avatarUrl' => $contributor['author']['avatar_url'],
'numCommits' => $contributor['total'],
'numAdditions' => $numAdditions,
'numDeletions' => $numDeletions,
];
}
}
return $projectContributorRows;
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSources;
use Doctrine\Website\DataSource\DataSource;
use Doctrine\Website\Projects\ProjectDataReader;
use function array_replace;
class Projects implements DataSource
{
private const DEFAULTS = [
'active' => true,
'archived' => false,
'hasDocs' => true,
'integration' => false,
];
/** @var ProjectDataReader */
private $projectDataReader;
/** @var mixed[][] */
private $projectsData = [];
/**
* @param mixed[][] $projectsData
*/
public function __construct(ProjectDataReader $projectDataReader, array $projectsData)
{
$this->projectDataReader = $projectDataReader;
$this->projectsData = $projectsData;
}
/**
* @return mixed[][]
*/
public function getSourceRows() : array
{
$projectRows = [];
foreach ($this->projectsData as $projectData) {
$projectRows[] = array_replace(
self::DEFAULTS,
$this->projectDataReader->read($projectData['repositoryName'])
);
}
return $projectRows;
}
}

View File

@@ -2,13 +2,12 @@
declare(strict_types=1);
namespace Doctrine\Website\Sitemap;
namespace Doctrine\Website\DataSources;
use DateTimeImmutable;
use Iterator;
use Doctrine\Website\DataSource\DataSource;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplHeap;
use function array_merge;
use function assert;
use function file_exists;
@@ -16,7 +15,7 @@ use function filemtime;
use function is_int;
use function str_replace;
class SitemapPageRepository
class SitemapPages implements DataSource
{
/** @var string */
private $sourcePath;
@@ -27,21 +26,26 @@ class SitemapPageRepository
}
/**
* @return SitemapPage[]
* @return mixed[][]
*/
public function findAll() : array
public function getSourceRows() : array
{
return array_merge(
[new SitemapPage('/', new DateTimeImmutable())],
$this->getUrlsFromFiles('projects'),
$this->getUrlsFromFiles('api')
[
[
'url' => '/',
'date' => new DateTimeImmutable(),
],
],
$this->getSitemapPagesDataFromFiles('projects'),
$this->getSitemapPagesDataFromFiles('api')
);
}
/**
* @return SitemapPage[]
* @return mixed[][]
*/
private function getUrlsFromFiles(string $path) : array
private function getSitemapPagesDataFromFiles(string $path) : array
{
$path = $this->sourcePath . '/' . $path;
@@ -56,23 +60,8 @@ class SitemapPageRepository
$it = new RecursiveIteratorIterator($it);
// Sorting the results so we get a consistent order in the sitemap
$sortedIterator = new class($it) extends SplHeap {
public function __construct(Iterator $iterator)
{
foreach ($iterator as $item) {
$this->insert($item);
}
}
public function compare(string $b, string $a) : int
{
return $a <=> $b;
}
};
$urls = [];
foreach ($sortedIterator as $file) {
foreach ($it as $file) {
$url = str_replace($this->sourcePath, '', $file);
$timestamp = filemtime($file);
@@ -80,7 +69,10 @@ class SitemapPageRepository
$date = (new DateTimeImmutable())->setTimestamp($timestamp);
$urls[] = new SitemapPage($url, $date);
$urls[] = [
'url' => $url,
'date' => $date,
];
}
return $urls;

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\DataSources;
use Doctrine\Website\DataSource\DataSource;
class TeamMembers implements DataSource
{
/** @var mixed[] */
private $teamMembers;
/**
* @param mixed[] $teamMembers
*/
public function __construct(array $teamMembers)
{
$this->teamMembers = $teamMembers;
}
/**
* @return mixed[][]
*/
public function getSourceRows() : array
{
return $this->teamMembers;
}
}

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\Website\Docs;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\ProcessFactory;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use function file_put_contents;
use function sprintf;
use function unlink;

View File

@@ -6,10 +6,11 @@ namespace Doctrine\Website\Docs;
use Doctrine\Website\Docs\RST\RSTBuilder;
use Doctrine\Website\Docs\RST\RSTLanguagesDetector;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Projects\ProjectDataRepository;
use Doctrine\Website\Projects\ProjectGitSyncer;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Repositories\ProjectRepository;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use function array_filter;
@@ -17,6 +18,9 @@ use function sprintf;
class BuildDocs
{
/** @var ProjectDataRepository */
private $projectDataRepository;
/** @var ProjectRepository */
private $projectRepository;
@@ -36,6 +40,7 @@ class BuildDocs
private $searchIndexer;
public function __construct(
ProjectDataRepository $projectDataRepository,
ProjectRepository $projectRepository,
ProjectGitSyncer $projectGitSyncer,
APIBuilder $apiBuilder,
@@ -43,12 +48,13 @@ class BuildDocs
RSTBuilder $rstBuilder,
SearchIndexer $searchIndexer
) {
$this->projectRepository = $projectRepository;
$this->projectGitSyncer = $projectGitSyncer;
$this->apiBuilder = $apiBuilder;
$this->rstLanguagesDetector = $rstLanguagesDetector;
$this->rstBuilder = $rstBuilder;
$this->searchIndexer = $searchIndexer;
$this->projectDataRepository = $projectDataRepository;
$this->projectRepository = $projectRepository;
$this->projectGitSyncer = $projectGitSyncer;
$this->apiBuilder = $apiBuilder;
$this->rstLanguagesDetector = $rstLanguagesDetector;
$this->rstBuilder = $rstBuilder;
$this->searchIndexer = $searchIndexer;
}
public function build(
@@ -63,9 +69,19 @@ class BuildDocs
$this->searchIndexer->initSearchIndex();
}
foreach ($this->projectDataRepository->getProjectRepositoryNames() as $repositoryName) {
if ($this->projectGitSyncer->isRepositoryInitialized($repositoryName)) {
continue;
}
$output->writeln(sprintf('Initializing <info>%s</info> repository', $repositoryName));
$this->projectGitSyncer->initRepository($repositoryName);
}
$projects = $this->projectRepository->findAll();
$this->initRepositories($projects);
$this->initDocsRepositories($output, $projects);
$projectsToBuild = $this->getProjectsToBuild($projects, $projectToBuild);
@@ -130,18 +146,22 @@ class BuildDocs
/**
* @param Project[] $projects
*/
private function initRepositories(array $projects) : void
private function initDocsRepositories(OutputInterface $output, array $projects) : void
{
foreach ($this->projectRepository->getProjectRepositoryNames() as $repositoryName) {
$this->projectGitSyncer->initRepository($repositoryName);
}
foreach ($projects as $project) {
if ($project->getRepositoryName() === $project->getDocsRepositoryName()) {
continue;
}
$this->projectGitSyncer->initRepository($project->getDocsRepositoryName());
$repositoryName = $project->getDocsRepositoryName();
if ($this->projectGitSyncer->isRepositoryInitialized($repositoryName)) {
continue;
}
$output->writeln(sprintf('Initializing <info>%s</info> repository', $repositoryName));
$this->projectGitSyncer->initRepository($repositoryName);
}
}

View File

@@ -6,8 +6,8 @@ namespace Doctrine\Website\Docs\RST;
use Doctrine\RST\Builder;
use Doctrine\RST\Document;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Symfony\Component\Filesystem\Filesystem;
class RSTBuilder

View File

@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Doctrine\Website\Docs\RST;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Symfony\Component\Filesystem\Filesystem;
use function file_exists;
use function preg_replace;

View File

@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Doctrine\Website\Docs\RST;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use function array_map;

View File

@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Doctrine\Website\Docs\RST;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Symfony\Component\Filesystem\Filesystem;
use function preg_match;
use function preg_replace;

View File

@@ -10,8 +10,8 @@ use Doctrine\RST\Document;
use Doctrine\RST\HTML\Nodes\ParagraphNode;
use Doctrine\RST\HTML\Nodes\TitleNode;
use Doctrine\RST\Nodes\Node;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use function get_class;
use function in_array;
use function md5;

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Github;
use Doctrine\Common\Cache\FilesystemCache;
use Doctrine\Website\Model\Project;
use Github\Api\Repo;
use Github\Client;
use function sprintf;
class GithubProjectContributors
{
/** @var FilesystemCache */
private $filesystemCache;
/** @var Client */
private $githubClient;
public function __construct(
FilesystemCache $filesystemCache,
Client $githubClient
) {
$this->filesystemCache = $filesystemCache;
$this->githubClient = $githubClient;
}
/**
* @return mixed[]
*/
public function getProjectContributors(Project $project) : array
{
$id = sprintf('doctrine-%s-contributors-data', $project->getSlug());
if ($this->filesystemCache->contains($id)) {
return $this->filesystemCache->fetch($id);
}
/** @var Repo $repo */
$repo = $this->githubClient->api('repo');
$contributors = $repo->statistics('doctrine', $project->getRepositoryName());
$this->filesystemCache->save($id, $contributors, 86400);
return $contributors;
}
}

View File

@@ -2,11 +2,15 @@
declare(strict_types=1);
namespace Doctrine\Website\Blog;
namespace Doctrine\Website\Model;
use DateTimeImmutable;
use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
class BlogPost
class BlogPost implements HydratableInterface, LoadMetadataInterface
{
/** @var string */
private $url;
@@ -42,6 +46,24 @@ class BlogPost
$this->date = $date;
}
public static function loadMetadata(ClassMetadataInterface $metadata) : void
{
$metadata->setIdentifier(['url']);
}
/**
* @param mixed[] $project
*/
public function hydrate(array $project, ObjectManagerInterface $objectManager) : void
{
$this->url = (string) $project['url'] ?? '';
$this->title = (string) $project['title'] ?? '';
$this->authorName = (string) $project['authorName'] ?? '';
$this->authorEmail = (string) $project['authorEmail'] ?? '';
$this->contents = (string) $project['contents'] ?? '';
$this->date = $project['date'] ?? new DateTimeImmutable();
}
public function getUrl() : string
{
return $this->url;

91
lib/Model/Contributor.php Normal file
View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Model;
use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
class Contributor implements HydratableInterface, LoadMetadataInterface
{
/** @var TeamMember|null */
private $teamMember;
/** @var string */
private $github;
/** @var string */
private $avatarUrl;
/** @var int */
private $numCommits;
/** @var int */
private $numAdditions;
/** @var int */
private $numDeletions;
/** @var Project[] */
private $projects;
public static function loadMetadata(ClassMetadataInterface $metadata) : void
{
$metadata->setIdentifier(['github']);
}
/**
* @param mixed[] $contributor
*/
public function hydrate(array $contributor, ObjectManagerInterface $objectManager) : void
{
$this->teamMember = $contributor['teamMember'] ?? null;
$this->github = (string) ($contributor['github'] ?? '');
$this->avatarUrl = (string) ($contributor['avatarUrl'] ?? '');
$this->numCommits = (int) ($contributor['numCommits'] ?? 0);
$this->numAdditions = (int) ($contributor['numAdditions'] ?? 0);
$this->numDeletions = (int) ($contributor['numDeletions'] ?? 0);
$this->projects = $contributor['projects'] ?? [];
}
public function getTeamMember() : ?TeamMember
{
return $this->teamMember;
}
public function getGithub() : string
{
return $this->github;
}
public function getAvatarUrl() : string
{
return $this->avatarUrl;
}
public function getNumCommits() : int
{
return $this->numCommits;
}
public function getNumAdditions() : int
{
return $this->numAdditions;
}
public function getNumDeletions() : int
{
return $this->numDeletions;
}
/**
* @return Project[]
*/
public function getProjects() : array
{
return $this->projects;
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Model;
use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
class DoctrineUser implements HydratableInterface, LoadMetadataInterface
{
/** @var string */
private $name;
/** @var string */
private $url;
public static function loadMetadata(ClassMetadataInterface $metadata) : void
{
$metadata->setIdentifier(['name']);
}
/**
* @param mixed[] $doctrineUser
*/
public function hydrate(array $doctrineUser, ObjectManagerInterface $objectManager) : void
{
$this->name = (string) $doctrineUser['name'];
$this->url = (string) $doctrineUser['url'];
}
public function getName() : string
{
return $this->name;
}
public function getUrl() : string
{
return $this->url;
}
}

View File

@@ -2,16 +2,23 @@
declare(strict_types=1);
namespace Doctrine\Website\Projects;
namespace Doctrine\Website\Model;
use Closure;
use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
use InvalidArgumentException;
use function array_filter;
use function array_values;
use function sprintf;
class Project
class Project implements HydratableInterface, LoadMetadataInterface
{
/** @var ProjectIntegrationType|null */
private $projectIntegrationType;
/** @var bool */
private $active;
@@ -67,6 +74,27 @@ class Project
* @param mixed[] $project
*/
public function __construct(array $project)
{
$this->doHydrate($project);
}
public static function loadMetadata(ClassMetadataInterface $metadata) : void
{
$metadata->setIdentifier(['slug']);
}
/**
* @param mixed[] $project
*/
public function hydrate(array $project, ObjectManagerInterface $objectManager) : void
{
$this->doHydrate($project);
}
/**
* @param mixed[] $project
*/
public function doHydrate(array $project) : void
{
$this->active = (bool) ($project['active'] ?? true);
$this->archived = (bool) ($project['archived'] ?? false);
@@ -76,9 +104,9 @@ class Project
$this->docsSlug = (string) ($project['docsSlug'] ?? $this->slug);
$this->composerPackageName = (string) ($project['composerPackageName'] ?? '');
$this->repositoryName = (string) ($project['repositoryName'] ?? '');
$this->hasDocs = $project['hasDocs'] ?? true;
$this->isIntegration = $project['isIntegration'] ?? false;
$this->integrationFor = $project['integrationFor'] ?? '';
$this->hasDocs = (bool) ($project['hasDocs'] ?? true);
$this->isIntegration = (bool) ($project['integration'] ?? false);
$this->integrationFor = (string) ($project['integrationFor'] ?? '');
$this->docsRepositoryName = (string) ($project['docsRepositoryName'] ?? $this->repositoryName);
$this->docsPath = (string) ($project['docsPath'] ?? '/docs');
$this->codePath = (string) ($project['codePath'] ?? '/lib');
@@ -94,6 +122,17 @@ class Project
? $version
: new ProjectVersion($version);
}
if (! $this->isIntegration) {
return;
}
$this->projectIntegrationType = new ProjectIntegrationType($project['integrationType']);
}
public function getProjectIntegrationType() : ?ProjectIntegrationType
{
return $this->projectIntegrationType;
}
public function isActive() : bool

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Model;
use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
class ProjectContributor implements HydratableInterface, LoadMetadataInterface
{
/** @var TeamMember|null */
private $teamMember;
/** @var string */
private $projectSlug;
/** @var Project */
private $project;
/** @var string */
private $github;
/** @var string */
private $avatarUrl;
/** @var int */
private $numCommits;
/** @var int */
private $numAdditions;
/** @var int */
private $numDeletions;
public static function loadMetadata(ClassMetadataInterface $metadata) : void
{
$metadata->setIdentifier(['projectSlug', 'github']);
}
/**
* @param mixed[] $projectContributor
*/
public function hydrate(array $projectContributor, ObjectManagerInterface $objectManager) : void
{
$this->teamMember = $projectContributor['teamMember'] ?? null;
$this->projectSlug = (string) ($projectContributor['projectSlug'] ?? '');
$this->project = $projectContributor['project'] ?? new Project([]);
$this->github = (string) ($projectContributor['github'] ?? '');
$this->avatarUrl = (string) ($projectContributor['avatarUrl'] ?? '');
$this->numCommits = (int) ($projectContributor['numCommits'] ?? 0);
$this->numAdditions = (int) ($projectContributor['numAdditions'] ?? 0);
$this->numDeletions = (int) ($projectContributor['numDeletions'] ?? 0);
}
public function getTeamMember() : ?TeamMember
{
return $this->teamMember;
}
public function isTeamMember() : bool
{
return $this->teamMember !== null;
}
public function getProjectSlug() : string
{
return $this->projectSlug;
}
public function getProject() : Project
{
return $this->project;
}
public function getGithub() : string
{
return $this->github;
}
public function getAvatarUrl() : string
{
return $this->avatarUrl;
}
public function getNumCommits() : int
{
return $this->numCommits;
}
public function getNumAdditions() : int
{
return $this->numAdditions;
}
public function getNumDeletions() : int
{
return $this->numDeletions;
}
}

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Doctrine\Website\Projects;
namespace Doctrine\Website\Model;
class ProjectIntegrationType
{
@@ -16,13 +16,13 @@ class ProjectIntegrationType
private $icon;
/**
* @param string[] $projectIntegrationType
* @param mixed[] $projectIntegrationType
*/
public function __construct(array $projectIntegrationType)
{
$this->name = $projectIntegrationType['name'] ?? '';
$this->url = $projectIntegrationType['url'] ?? '';
$this->icon = $projectIntegrationType['icon'] ?? '';
$this->name = (string) ($projectIntegrationType['name'] ?? '');
$this->url = (string) ($projectIntegrationType['url'] ?? '');
$this->icon = (string) ($projectIntegrationType['icon'] ?? '');
}
public function getName() : string

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Doctrine\Website\Projects;
namespace Doctrine\Website\Model;
use function array_merge;

50
lib/Model/SitemapPage.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Model;
use DateTimeImmutable;
use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
class SitemapPage implements HydratableInterface, LoadMetadataInterface
{
/** @var string */
private $url;
/** @var DateTimeImmutable */
private $date;
public function __construct(string $url, DateTimeImmutable $date)
{
$this->url = $url;
$this->date = $date;
}
public static function loadMetadata(ClassMetadataInterface $metadata) : void
{
$metadata->setIdentifier(['url']);
}
/**
* @param mixed[] $sitemapPage
*/
public function hydrate(array $sitemapPage, ObjectManagerInterface $objectManager) : void
{
$this->url = (string) ($sitemapPage['url'] ?? '');
$this->date = $sitemapPage['date'] ?? new DateTimeImmutable();
}
public function getUrl() : string
{
return $this->url;
}
public function getDate() : DateTimeImmutable
{
return $this->date;
}
}

89
lib/Model/TeamMember.php Normal file
View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Model;
use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
use function in_array;
class TeamMember implements HydratableInterface, LoadMetadataInterface
{
/** @var string */
private $name;
/** @var string */
private $github;
/** @var string */
private $twitter;
/** @var string */
private $avatarUrl;
/** @var string */
private $website;
/** @var string */
private $location;
/** @var string[] */
private $maintains = [];
public static function loadMetadata(ClassMetadataInterface $metadata) : void
{
$metadata->setIdentifier(['github']);
}
/**
* @param mixed[] $teamMember
*/
public function hydrate(array $teamMember, ObjectManagerInterface $objectManager) : void
{
$this->name = (string) ($teamMember['name'] ?? '');
$this->github = (string) ($teamMember['github'] ?? '');
$this->twitter = (string) ($teamMember['twitter'] ?? '');
$this->avatarUrl = (string) ($teamMember['avatarUrl'] ?? '');
$this->website = (string) ($teamMember['website'] ?? '');
$this->location = (string) ($teamMember['location'] ?? '');
$this->maintains = $teamMember['maintains'] ?? [];
}
public function getName() : string
{
return $this->name;
}
public function getGithub() : string
{
return $this->github;
}
public function getTwitter() : string
{
return $this->twitter;
}
public function getAvatarUrl() : string
{
return $this->avatarUrl;
}
public function getWebsite() : string
{
return $this->website;
}
public function getLocation() : string
{
return $this->location;
}
public function isProjectMaintainer(Project $project) : bool
{
return in_array($project->getSlug(), $this->maintains, true);
}
}

View File

@@ -59,7 +59,7 @@ class ProjectDataReader
$this->readJsonFile($repositoryName, self::DOCTRINE_PROJECT_JSON_FILE_NAME)
);
if (isset($projectData['isIntegration']) && $projectData['isIntegration'] === true) {
if (isset($projectData['integration']) && $projectData['integration'] === true) {
if (! isset($projectData['integrationType'])) {
throw new InvalidArgumentException(sprintf(
'Project integration %s requires a type.',
@@ -78,6 +78,10 @@ class ProjectDataReader
$projectData['integrationType'] = $this->projectIntegrationTypes[$projectData['integrationType']];
}
if (! isset($projectData['docsSlug'])) {
$projectData['docsSlug'] = $projectData['slug'];
}
return $projectData;
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Projects;
class ProjectDataRepository
{
/** @var mixed[][] */
private $projectsData = [];
/**
* @param mixed[][] $projectsData
*/
public function __construct(array $projectsData)
{
$this->projectsData = $projectsData;
}
/**
* @return string[]
*/
public function getProjectRepositoryNames() : array
{
$projectRepositoryNames = [];
foreach ($this->projectsData as $projectData) {
$projectRepositoryNames[] = $projectData['repositoryName'];
}
return $projectRepositoryNames;
}
}

View File

@@ -1,27 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Projects;
class ProjectFactory
{
/** @var ProjectDataReader */
private $projectDataReader;
public function __construct(ProjectDataReader $projectDataReader)
{
$this->projectDataReader = $projectDataReader;
}
public function create(string $repositoryName) : Project
{
$projectData = $this->projectDataReader->read($repositoryName);
if (isset($projectData['isIntegration']) && $projectData['isIntegration'] === true) {
return new ProjectIntegration($projectData);
}
return new Project($projectData);
}
}

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Doctrine\Website\Projects;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\ProcessFactory;
use function is_dir;
use function sprintf;
@@ -22,6 +24,11 @@ class ProjectGitSyncer
$this->projectsPath = $projectsPath;
}
public function isRepositoryInitialized(string $repositoryName) : bool
{
return is_dir($this->projectsPath . '/' . $repositoryName);
}
public function initRepository(string $repositoryName) : void
{
$repositoryPath = $this->projectsPath . '/' . $repositoryName;

View File

@@ -1,36 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Projects;
use InvalidArgumentException;
use function sprintf;
class ProjectIntegration extends Project
{
/** @var ProjectIntegrationType */
private $type;
/**
* @param mixed[] $project
*/
public function __construct(array $project)
{
parent::__construct($project);
if (! isset($project['integrationType'])) {
throw new InvalidArgumentException(sprintf(
'Project integration %s requires a type.',
$this->getName()
));
}
$this->type = new ProjectIntegrationType($project['integrationType']);
}
public function getType() : ProjectIntegrationType
{
return $this->type;
}
}

View File

@@ -1,134 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Projects;
use InvalidArgumentException;
use function array_filter;
use function array_map;
use function sprintf;
use function usort;
class ProjectRepository
{
/** @var mixed[][] */
private $projectsData = [];
/** @var ProjectFactory */
private $projectFactory;
/** @var Project[] */
private $projects = [];
/**
* @param mixed[][] $projectsData
*/
public function __construct(array $projectsData, ProjectFactory $projectFactory)
{
$this->projectsData = $projectsData;
$this->projectFactory = $projectFactory;
}
/**
* @return string[]
*/
public function getProjectRepositoryNames() : array
{
$projectRepositoryNames = [];
foreach ($this->projectsData as $projectData) {
$projectRepositoryNames[] = $projectData['repositoryName'];
}
return $projectRepositoryNames;
}
public function findOneBySlug(string $slug) : Project
{
$this->init();
foreach ($this->projects as $project) {
if ($project->getSlug() === $slug || $project->getDocsSlug() === $slug) {
return $project;
}
}
throw new InvalidArgumentException(sprintf('Could not find Project with slug "%s"', $slug));
}
/**
* @return Project[]
*/
public function findAll() : array
{
$this->init();
return $this->projects;
}
/**
* @return Project[]
*/
public function findPrimaryProjects() : array
{
return array_filter($this->findAll(), static function (Project $project) : bool {
return $project->isActive() && ! $project->isIntegration();
});
}
/**
* @return Project[]
*/
public function findInactiveProjects() : array
{
return array_filter($this->findAll(), static function (Project $project) : bool {
return ! $project->isActive() && ! $project->isArchived();
});
}
/**
* @return Project[]
*/
public function findArchivedProjects() : array
{
return array_filter($this->findAll(), static function (Project $project) : bool {
return ! $project->isActive() && $project->isArchived();
});
}
/**
* @return Project[]
*/
public function findIntegrationProjects() : array
{
return array_filter($this->findAll(), static function (Project $project) : bool {
return $project->isActive() && $project->isIntegration();
});
}
/**
* @return Project[]
*/
public function findProjectIntegrations(Project $project) : array
{
return array_filter($this->findAll(), static function (Project $p) use ($project) : bool {
return $p->isIntegration() && $p->getIntegrationFor() === $project->getSlug();
});
}
private function init() : void
{
if ($this->projects !== []) {
return;
}
$this->projects = array_map(function (array $projectData) : Project {
return $this->projectFactory->create($projectData['repositoryName']);
}, $this->projectsData);
usort($this->projects, static function (Project $a, Project $b) {
return $a->getName() <=> $b->getName();
});
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Repositories;
use Doctrine\SkeletonMapper\ObjectRepository\BasicObjectRepository;
use Doctrine\Website\Model\BlogPost;
use InvalidArgumentException;
class BlogPostRepository extends BasicObjectRepository
{
/**
* @return BlogPost[]
*/
public function findAll() : array
{
return $this->findBy([], ['date' => 'desc']);
}
/**
* @return BlogPost[]
*/
public function findPaginated(int $page = 1, int $perPage = 10) : array
{
if ($page < 1 || $perPage < 1) {
throw new InvalidArgumentException('Pagination parameters must be positive.');
}
$offset = ($page - 1) * $perPage;
return $this->findBy([], ['date' => 'desc'], $perPage, $offset);
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Repositories;
use Doctrine\SkeletonMapper\ObjectRepository\BasicObjectRepository;
use Doctrine\Website\Model\Contributor;
class ContributorRepository extends BasicObjectRepository
{
/**
* @return Contributor[]
*/
public function findMaintainers() : array
{
return $this->findBy(['isTeamMember' => true], ['github' => 'asc']);
}
/**
* @return Contributor[]
*/
public function findContributors() : array
{
return $this->findBy(['isTeamMember' => false], ['github' => 'asc']);
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Repositories;
use Doctrine\SkeletonMapper\ObjectRepository\BasicObjectRepository;
use Doctrine\Website\Model\DoctrineUser;
class DoctrineUserRepository extends BasicObjectRepository
{
/**
* @return DoctrineUser[]
*/
public function findAll() : array
{
/** @var DoctrineUser[] $doctrineUsers */
$doctrineUsers = parent::findAll();
return $doctrineUsers;
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Repositories;
use Doctrine\SkeletonMapper\ObjectRepository\BasicObjectRepository;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectContributor;
class ProjectContributorRepository extends BasicObjectRepository
{
/**
* @return ProjectContributor[]
*/
public function findAll() : array
{
/** @var ProjectContributor[] $projectContributors */
$projectContributors = $this->findBy([], ['github' => 'asc']);
return $projectContributors;
}
/**
* @return ProjectContributor[]
*/
public function findMaintainersByProject(Project $project) : array
{
return $this->findBy([
'isMaintainer' => true,
'projectSlug' => $project->getSlug(),
], ['github' => 'asc']);
}
/**
* @return ProjectContributor[]
*/
public function findContributorsByProject(Project $project) : array
{
return $this->findBy([
'isMaintainer' => false,
'projectSlug' => $project->getSlug(),
], ['github' => 'asc']);
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Repositories;
use Doctrine\SkeletonMapper\ObjectRepository\BasicObjectRepository;
use Doctrine\Website\Model\Project;
use InvalidArgumentException;
use function sprintf;
class ProjectRepository extends BasicObjectRepository
{
/**
* @return Project[]
*/
public function findAll() : array
{
/** @var Project[] $projects */
$projects = parent::findAll();
return $projects;
}
public function findOneByDocsSlug(string $docsSlug) : Project
{
$project = $this->findOneBy(['docsSlug' => $docsSlug]);
if ($project === null) {
throw new InvalidArgumentException(sprintf('Could not find Project with docsSlug "%s"', $docsSlug));
}
return $project;
}
/**
* @return Project[]
*/
public function findPrimaryProjects() : array
{
return $this->findBy([
'active' => true,
'integration' => false,
], ['name' => 'asc']);
}
/**
* @return Project[]
*/
public function findInactiveProjects() : array
{
return $this->findBy([
'active' => false,
'archived' => false,
], ['name' => 'asc']);
}
/**
* @return Project[]
*/
public function findArchivedProjects() : array
{
return $this->findBy([
'active' => false,
'archived' => true,
], ['name' => 'asc']);
}
/**
* @return Project[]
*/
public function findIntegrationProjects() : array
{
return $this->findBy([
'active' => true,
'integration' => true,
], ['name' => 'asc']);
}
/**
* @return Project[]
*/
public function findProjectIntegrations(Project $project) : array
{
return $this->findBy([
'integration' => true,
'integrationFor' => $project->getSlug(),
], ['name' => 'asc']);
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Repositories;
use Doctrine\SkeletonMapper\ObjectRepository\BasicObjectRepository;
use Doctrine\Website\Model\SitemapPage;
class SitemapPageRepository extends BasicObjectRepository
{
/**
* @return SitemapPage[]
*/
public function findAll() : array
{
/** @var SitemapPage[] $sitemapPages */
$sitemapPages = $this->findBy([], ['url' => 'asc']);
return $sitemapPages;
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Repositories;
use Doctrine\SkeletonMapper\ObjectRepository\BasicObjectRepository;
use Doctrine\Website\Model\TeamMember;
class TeamMemberRepository extends BasicObjectRepository
{
public function findOneByGithub(string $github) : ?TeamMember
{
return $this->findOneBy(['github' => $github]);
}
}

View File

@@ -1,32 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Sitemap;
use DateTimeImmutable;
class SitemapPage
{
/** @var string */
private $url;
/** @var DateTimeImmutable */
private $date;
public function __construct(string $url, DateTimeImmutable $date)
{
$this->url = $url;
$this->date = $date;
}
public function getUrl() : string
{
return $this->url;
}
public function getDate() : DateTimeImmutable
{
return $this->date;
}
}

View File

@@ -1,119 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Team;
use Closure;
use Doctrine\Website\Projects\Project;
use function array_filter;
use function in_array;
use function ksort;
class TeamRepository
{
/** @var mixed[] */
private $teamMembers;
/**
* @param mixed[] $teamMembers
*/
public function __construct(array $teamMembers)
{
$this->teamMembers = $teamMembers;
}
/**
* @return mixed[]
*/
public function getTeamMembers(?Closure $filter = null) : array
{
$teamMembers = [];
foreach ($this->teamMembers as $key => $teamMember) {
$name = $teamMember['name'] ?? $key;
$teamMembers[$name] = $teamMember;
}
if ($filter !== null) {
$teamMembers = array_filter($teamMembers, $filter);
}
ksort($teamMembers);
return $teamMembers;
}
/**
* @return mixed[]
*/
public function getActiveCoreTeamMembers() : array
{
return $this->getTeamMembers(static function (array $teamMember) {
$active = $teamMember['active'] ?? false;
$core = $teamMember['core'] ?? false;
return $active && $core;
});
}
/**
* @return mixed[]
*/
public function getActiveDocumentationTeamMembers() : array
{
return $this->getTeamMembers(static function (array $teamMember) {
$active = $teamMember['active'] ?? false;
$documentation = $teamMember['documentation'] ?? false;
return $active && $documentation;
});
}
/**
* @return mixed[]
*/
public function getInactiveTeamMembers() : array
{
return $this->getTeamMembers(static function (array $teamMember) {
$active = $teamMember['active'] ?? false;
return $active === false;
});
}
/**
* @return mixed[]
*/
public function getAllProjectTeamMembers(Project $project) : array
{
return $this->getTeamMembers(static function (array $teamMember) use ($project) {
return in_array($project->getSlug(), $teamMember['projects'] ?? [], true);
});
}
/**
* @return mixed[]
*/
public function getActiveProjectTeamMembers(Project $project) : array
{
return $this->getTeamMembers(static function (array $teamMember) use ($project) {
$active = $teamMember['active'] ?? false;
return $active && in_array($project->getSlug(), $teamMember['projects'] ?? [], true);
});
}
/**
* @return mixed[]
*/
public function getInactiveProjectTeamMembers(Project $project) : array
{
return $this->getTeamMembers(static function (array $teamMember) use ($project) {
$active = $teamMember['active'] ?? false;
return ! $active && in_array($project->getSlug(), $teamMember['projects'] ?? [], true);
});
}
}

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Doctrine\Website\Twig;
use Doctrine\Website\Assets\AssetIntegrityGenerator;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Model\Project;
use Parsedown;
use Twig_Extension;
use Twig_SimpleFilter;

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\Website\Twig;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Repositories\ProjectRepository;
use Twig_Extension;
use Twig_SimpleFunction;
use function file_exists;
@@ -39,9 +39,9 @@ class ProjectExtension extends Twig_Extension
];
}
public function getProject(string $slug) : Project
public function getProject(string $docsSlug) : Project
{
return $this->projectRepository->findOneBySlug($slug);
return $this->projectRepository->findOneByDocsSlug($docsSlug);
}
public function getUrlVersion(ProjectVersion $projectVersion, string $url, string $currentVersion) : ?string

View File

@@ -6,9 +6,9 @@ namespace Doctrine\Website;
use Doctrine\Website\Builder\SourceFileBuilder;
use Doctrine\Website\Builder\SourceFileRepository;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Repositories\ProjectRepository;
use RuntimeException;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
@@ -122,7 +122,7 @@ class WebsiteBuilder
throw new RuntimeException(sprintf(
'Failed building file "%s" with error "%s',
$file->getWritePath(),
$e->getMessage()
$e->getMessage() . "\n\n" . $e->getTraceAsString()
));
}
}
@@ -130,6 +130,7 @@ class WebsiteBuilder
private function createProjectVersionAliases(string $buildDir) : void
{
/** @var Project[] $projects */
$projects = $this->projectRepository->findAll();
foreach ($projects as $project) {

View File

@@ -70,13 +70,13 @@ controller: ['Doctrine\Website\Controllers\HomepageController', 'index']
<p class="card-text">Many other popular PHP projects use libraries from Doctrine. Some use the full <a href="/projects/orm.html" class="text-white font-weight-bold">ORM</a> and others may only use one small library such as the <a href="/projects/inflector.html" class="text-white font-weight-bold">Inflector</a>. Here are some projects that use Doctrine.</p>
</div>
<table id="who-uses-doctrine-table" class="table table-striped table-responsive-xl">
{% for batch in whoUsesDoctrine|batch(3, 'and many more&hellip;') %}
{% for batch in doctrineUsers|batch(3, 'and many more&hellip;') %}
<tr>
{% for project in batch %}
{% if project.url is defined %}
<td><a href="{{ project.url }}" target="_blank" class="mr-4">{{ project.name }}</a></td>
{% for doctrineUser in batch %}
{% if doctrineUser.url is defined %}
<td><a href="{{ doctrineUser.url }}" target="_blank" class="mr-4">{{ doctrineUser.name }}</a></td>
{% else %}
<td><span class="font-italic">{{ project|raw }}</span></a></td>
<td><span class="font-italic">{{ doctrineUser|raw }}</span></a></td>
{% endif %}
{% endfor %}
</tr>

View File

@@ -1,23 +0,0 @@
---
layout: default
title: Doctrine Team
menuSlug: team
permalink: /team/index.html
controller: ['Doctrine\Website\Controllers\TeamController', 'index']
---
<h1>Core Team</h1>
<div class="row">
{% include "team-members-list.html.twig" with {members:activeCoreTeamMembers} %}
</div>
<h1>Documentation Team</h1>
<div class="row">
{% include "team-members-list.html.twig" with {members:activeDocumentationTeamMembers} %}
</div>
<h3>Inactive</h3>
{% include "team-members-short-list.html.twig" with {members:inactiveTeamMembers} %}

View File

@@ -0,0 +1,18 @@
---
layout: default
title: Doctrine Contributors
menuSlug: team
permalink: /team/contributors.html
controller: ['Doctrine\Website\Controllers\TeamController', 'contributors']
---
<h1>Contributors</h1>
<p class="lead">
In addition to the <a href="{{ site.url }}/team/maintainers.html">core maintainers</a>, Doctrine has received contributions from {{ contributors|length }}
different people from all over the world. Could you be the next contributor? Head on over to the
<a href="{{ site.url }}/contribute/">Contributor Workflow</a> to learn how to
contribute to your favorite Doctrine project today!
</p>
{% include "contributors-list.html.twig" with {contributors:contributors} %}

View File

@@ -0,0 +1,17 @@
---
layout: default
title: Doctrine Maintainers
menuSlug: team
permalink: /team/maintainers.html
controller: ['Doctrine\Website\Controllers\TeamController', 'maintainers']
---
<h1>Maintainers</h1>
<p class="lead">
Doctrine is maintained by a core group of {{ contributors|length }} developers. Each maintainer has
their own areas of responsibilities and together we are able to keep the various Doctrine projects
moving forward as the PHP ecosystem evolves.
</p>
{% include "maintainers-list.html.twig" with {contributors:contributors} %}

View File

@@ -0,0 +1,27 @@
<ul class="list-group">
{% for contributor in contributors %}
<li class="list-group-item">
<img src="{{ contributor.avatarUrl }}" class="rounded float-left mr-5" style="width: 50px;" />
<strong>
<a href="https://github.com/{{ contributor.github }}/" target="_blank">{{ contributor.github }}</a>
</strong>
{% if contributor.teamMember %}
<ul class="list-inline">
{% if contributor.teamMember.twitter %}
<li class="list-inline-item" target="_blank"><a href="https://twitter.com/{{ contributor.teamMember.twitter }}/" target="_blank"><i class="fab fa-twitter mr-2"></i></a></li>
{% endif %}
{% if contributor.teamMember.website %}
<li class="list-inline-item" target="_blank"><a href="{{ contributor.teamMember.website }}" target="_blank"><i class="fas fa-link mr-2"></i></a></li>
{% endif %}
{% if contributor.teamMember.github %}
<li class="list-inline-item" target="_blank"><a href="https://github.com/{{ contributor.teamMember.github }}/" target="_blank"><i class="fab fa-github mr-2"></i></a></li>
{% endif %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>

View File

@@ -184,8 +184,13 @@
<li class="nav-item{% if menuSlug == 'blog' %} active{% endif %}">
<a class="nav-link" href="{{ site.url }}/blog/">Blog</a>
</li>
<li class="nav-item{% if menuSlug == 'team' %} active{% endif %}">
<a class="nav-link" href="{{ site.url }}/team/">Team</a>
<li class="nav-item dropdown{% if menuSlug == 'team' %} active{% endif %}">
<a class="nav-link dropdown-toggle" href="{{ site.url }}/team/maintainers.html" id="navbarTeamDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Team</a>
<div class="dropdown-menu" aria-labelledby="navbarTeamDropdown">
<a class="dropdown-item" href="{{ site.url }}/team/maintainers.html">Maintainers</a>
<a class="dropdown-item" href="{{ site.url }}/team/contributors.html">Contributors</a>
</div>
</li>
</ul>

View File

@@ -78,26 +78,15 @@
{% include "projects-short-list.html.twig" with {projects:integrationProjects} %}
{% endif %}
{% if project.archived or not project.active %}
{% if allTeamMembers %}
<h2 class="mt-4">Team</h2>
{% if maintainers|length > 0 %}
<h2 class="mt-4">Maintainers</h2>
{% include "team-members-short-list.html.twig" with {members:allTeamMembers} %}
{% endif %}
{% include "maintainers-list.html.twig" with {contributors:maintainers} %}
{% endif %}
{% else %}
{% if activeTeamMembers %}
<h2 class="mt-4">Team</h2>
{% include "team-members-short-list.html.twig" with {members:activeTeamMembers} %}
{% endif %}
{% if inactiveTeamMembers %}
<h2 class="mt-4">Inactive</h2>
{% include "team-members-short-list.html.twig" with {members:inactiveTeamMembers} %}
{% endif %}
{% if contributors|length > 0 %}
<h2 class="mt-4">Contributors</h2>
{% include "contributors-list.html.twig" with {contributors:contributors} %}
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,33 @@
<ul class="list-group">
{% for contributor in contributors %}
<li class="list-group-item">
<img src="{{ contributor.avatarUrl }}" class="rounded float-left mr-5" style="width: 50px;" />
<strong>
<a href="https://github.com/{{ contributor.github }}/" target="_blank">
{{ contributor.teamMember.name ?? member.github }}
{% if contributor.teamMember.name %}
<small>({{ contributor.teamMember.github }})</small>
{% endif %}
</a>
</strong>
{% if contributor.teamMember %}
<ul class="list-inline">
{% if contributor.teamMember.twitter %}
<li class="list-inline-item" target="_blank"><a href="https://twitter.com/{{ contributor.teamMember.twitter }}/" target="_blank"><i class="fab fa-twitter mr-2"></i></a></li>
{% endif %}
{% if contributor.teamMember.website %}
<li class="list-inline-item" target="_blank"><a href="{{ contributor.teamMember.website }}" target="_blank"><i class="fas fa-link mr-2"></i></a></li>
{% endif %}
{% if contributor.teamMember.github %}
<li class="list-inline-item" target="_blank"><a href="https://github.com/{{ contributor.teamMember.github }}/" target="_blank"><i class="fab fa-github mr-2"></i></a></li>
{% endif %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>

View File

@@ -3,7 +3,7 @@
<li class="list-group-item">
<h5 class="card-title">
{% if project.isIntegration %}
<a href="{{ project.type.url }}" target="_blank"><img src="{{ project.type.icon }}" title="{{ project.type.name }}" width="30" class="mr-2" /></a>
<a href="{{ project.projectIntegrationType.url }}" target="_blank"><img src="{{ project.projectIntegrationType.icon }}" title="{{ project.projectIntegrationType.name }}" width="30" class="mr-2" /></a>
{% endif %}
<a href="{{ site.url }}/projects/{{ project.slug }}.html">{{ project.name }}</a>

View File

@@ -1,26 +0,0 @@
{% for member in members %}
<div class="col-md-3">
<div class="card mb-4 box-shadow">
<img class="card-img-top" src="{{ member.avatarUrl }}" alt="{{ member.name }}">
<div class="card-body">
<h5 class="card-title">{{ member.name ?? member.github }}</h5>
<div class="d-flex justify-content-between align-items-center">
<ul class="list-inline">
{% if member.twitter is defined and member.twitter %}
<li class="list-inline-item" target="_blank"><a href="https://twitter.com/{{ member.twitter }}/" target="_blank"><i class="fab fa-twitter mr-2"></i></a></li>
{% endif %}
{% if member.website is defined and member.website %}
<li class="list-inline-item" target="_blank"><a href="{{ member.website }}" target="_blank"><i class="fas fa-link mr-2"></i></a></li>
{% endif %}
{% if member.github is defined and member.github %}
<li class="list-inline-item" target="_blank"><a href="https://github.com/{{ member.github }}/" target="_blank"><i class="fab fa-github mr-2"></i></a></li>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
{% endfor %}

View File

@@ -1,22 +0,0 @@
<ul class="list-group">
{% for member in members %}
<li class="list-group-item">
<img src="{{ member.avatarUrl }}" class="rounded float-left mr-5" style="width: 50px;" />
<strong>{{ member.name ?? member.github }}{% if member.name %} <small>({{ member.github }}){% endif %}</small></strong>
<ul class="list-inline">
{% if member.twitter is defined and member.twitter %}
<li class="list-inline-item" target="_blank"><a href="https://twitter.com/{{ member.twitter }}/" target="_blank"><i class="fab fa-twitter mr-2"></i></a></li>
{% endif %}
{% if member.website is defined and member.website %}
<li class="list-inline-item" target="_blank"><a href="{{ member.website }}" target="_blank"><i class="fas fa-link mr-2"></i></a></li>
{% endif %}
{% if member.github is defined and member.github %}
<li class="list-inline-item" target="_blank"><a href="https://github.com/{{ member.github }}/" target="_blank"><i class="fab fa-github mr-2"></i></a></li>
{% endif %}
</ul>
</li>
{% endfor %}
</ul>

View File

@@ -1,165 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\Blog;
use Doctrine\Website\Blog\BlogPostRepository;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Builder\SourceFileParameters;
use Doctrine\Website\Builder\SourceFileRepository;
use Doctrine\Website\Tests\TestCase;
use InvalidArgumentException;
use PHPUnit\Framework\MockObject\MockObject;
class BlogPostRepositoryTest extends TestCase
{
/** @var SourceFileRepository|MockObject */
private $sourceFileRepository;
/** @var BlogPostRepository */
private $blogPostRepository;
public function testFind() : void
{
$sourceFile = new SourceFile(
'md',
'/tmp/test.html',
'/tmp/test.html',
'test',
new SourceFileParameters([
'url' => '/2018/09/01/test.html',
'title' => 'Test Blog Post',
'authorName' => 'Jonathan H. Wage',
'authorEmail' => 'jonwage@gmail.com',
])
);
$blogPost = $this->blogPostRepository->find(
$sourceFile
);
self::assertSame('/2018/09/01/test.html', $blogPost->getUrl());
self::assertSame('Test Blog Post', $blogPost->getTitle());
self::assertSame('Jonathan H. Wage', $blogPost->getAuthorName());
self::assertSame('jonwage@gmail.com', $blogPost->getAuthorEmail());
self::assertSame('test', $blogPost->getContents());
self::assertSame('2018-09-01', $blogPost->getDate()->format('Y-m-d'));
}
public function testFindPaginatedPageOne() : void
{
$this->setUpFindAll();
$blogPosts = $this->blogPostRepository->findPaginated(1, 1);
self::assertSame('/2018/09/02/test1.html', $blogPosts[0]->getUrl());
self::assertSame('Test Blog Post', $blogPosts[0]->getTitle());
self::assertSame('Jonathan H. Wage', $blogPosts[0]->getAuthorName());
self::assertSame('jonwage@gmail.com', $blogPosts[0]->getAuthorEmail());
self::assertSame('test1', $blogPosts[0]->getContents());
self::assertSame('2018-09-02', $blogPosts[0]->getDate()->format('Y-m-d'));
self::assertCount(1, $blogPosts);
}
public function testFindPaginatedPageTwo() : void
{
$this->setUpFindAll();
$blogPosts = $this->blogPostRepository->findPaginated(2, 1);
self::assertSame('/2018/09/01/test2.html', $blogPosts[0]->getUrl());
self::assertSame('Test Blog Post', $blogPosts[0]->getTitle());
self::assertSame('Jonathan H. Wage', $blogPosts[0]->getAuthorName());
self::assertSame('jonwage@gmail.com', $blogPosts[0]->getAuthorEmail());
self::assertSame('test2', $blogPosts[0]->getContents());
self::assertSame('2018-09-01', $blogPosts[0]->getDate()->format('Y-m-d'));
self::assertCount(1, $blogPosts);
}
public function testFindPaginatedWithNegativePageThrowsInvalidArgumentException() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Pagination parameters must be positive.');
$this->blogPostRepository->findPaginated(-1, 10);
}
public function testFindPaginatedWithNegativePerPageThrowsInvalidArgumentException() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Pagination parameters must be positive.');
$this->blogPostRepository->findPaginated(1, -10);
}
public function testFindAll() : void
{
$this->setUpFindAll();
$blogPosts = $this->blogPostRepository->findAll();
self::assertCount(2, $blogPosts);
self::assertSame('/2018/09/02/test1.html', $blogPosts[0]->getUrl());
self::assertSame('Test Blog Post', $blogPosts[0]->getTitle());
self::assertSame('Jonathan H. Wage', $blogPosts[0]->getAuthorName());
self::assertSame('jonwage@gmail.com', $blogPosts[0]->getAuthorEmail());
self::assertSame('test1', $blogPosts[0]->getContents());
self::assertSame('2018-09-02', $blogPosts[0]->getDate()->format('Y-m-d'));
self::assertSame('/2018/09/01/test2.html', $blogPosts[1]->getUrl());
self::assertSame('Test Blog Post', $blogPosts[1]->getTitle());
self::assertSame('Jonathan H. Wage', $blogPosts[1]->getAuthorName());
self::assertSame('jonwage@gmail.com', $blogPosts[1]->getAuthorEmail());
self::assertSame('test2', $blogPosts[1]->getContents());
self::assertSame('2018-09-01', $blogPosts[1]->getDate()->format('Y-m-d'));
}
protected function setUp() : void
{
$this->sourceFileRepository = $this->createMock(SourceFileRepository::class);
$this->blogPostRepository = new BlogPostRepository(
$this->sourceFileRepository
);
}
private function setUpFindAll() : void
{
$sourceFile1 = new SourceFile(
'md',
'/tmp/test1.html',
'/tmp/test1.html',
'test1',
new SourceFileParameters([
'url' => '/2018/09/02/test1.html',
'title' => 'Test Blog Post',
'authorName' => 'Jonathan H. Wage',
'authorEmail' => 'jonwage@gmail.com',
])
);
$sourceFile2 = new SourceFile(
'md',
'/tmp/test.html',
'/tmp/test.html',
'test2',
new SourceFileParameters([
'url' => '/2018/09/01/test2.html',
'title' => 'Test Blog Post',
'authorName' => 'Jonathan H. Wage',
'authorEmail' => 'jonwage@gmail.com',
])
);
$files = [$sourceFile1, $sourceFile2];
$this->sourceFileRepository->expects(self::once())
->method('getFiles')
->with('', 'source/blog')
->willReturn($files);
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSource;
use Doctrine\Website\DataSource\CriteriaMatcher;
use PHPUnit\Framework\TestCase;
class CriteriaMatcherTest extends TestCase
{
public function testEqualsTrue() : void
{
$criteriaMatcher = new CriteriaMatcher(
['username' => 'jwage'],
['username' => 'jwage']
);
self::assertTrue($criteriaMatcher->matches());
}
public function testEqualsFalse() : void
{
$criteriaMatcher = new CriteriaMatcher(
['username' => 'jwage'],
['username' => 'jonwage']
);
self::assertFalse($criteriaMatcher->matches());
}
public function testContainsTrue() : void
{
$criteriaMatcher = new CriteriaMatcher(
['projects' => ['$contains' => 'dbal']],
['projects' => ['orm', 'dbal']]
);
self::assertTrue($criteriaMatcher->matches());
}
public function testContainsFalse() : void
{
$criteriaMatcher = new CriteriaMatcher(
['projects' => ['$contains' => 'mongodb-odm']],
['projects' => ['orm', 'dbal']]
);
self::assertFalse($criteriaMatcher->matches());
}
}

View File

@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSource;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
use Doctrine\Website\DataSource\DataSource;
use Doctrine\Website\DataSource\DataSourceObjectDataRepository;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use stdClass;
class DataSourceObjectDataRepositoryTest extends TestCase
{
/** @var ObjectManagerInterface|MockObject */
private $objectManager;
/** @var DataSource|MockObject */
private $dataSource;
/** @var DataSourceObjectDataRepository */
private $dataSourceObjectDataRepository;
protected function setUp() : void
{
$this->objectManager = $this->createMock(ObjectManagerInterface::class);
$this->dataSource = $this->createMock(DataSource::class);
$this->dataSourceObjectDataRepository = new DataSourceObjectDataRepository(
$this->objectManager,
$this->dataSource,
stdClass::class
);
}
public function testFindAll() : void
{
$rows = [
['row' => 1],
['row' => 2],
];
$this->dataSource->expects(self::once())
->method('getSourceRows')
->willReturn($rows);
self::assertEquals($rows, $this->dataSourceObjectDataRepository->findAll());
}
public function testFindByCriteria() : void
{
$rows = [
['username' => 'jwage'],
['username' => 'ocramius'],
];
$this->dataSource->expects(self::once())
->method('getSourceRows')
->willReturn($rows);
self::assertEquals([
['username' => 'ocramius'],
], $this->dataSourceObjectDataRepository->findBy(['username' => 'ocramius']));
}
public function testFindByOrderBy() : void
{
$rows = [
['username' => 'jwage'],
['username' => 'ocramius'],
];
$this->dataSource->expects(self::once())
->method('getSourceRows')
->willReturn($rows);
self::assertEquals([
['username' => 'ocramius'],
['username' => 'jwage'],
], $this->dataSourceObjectDataRepository->findBy([], ['username' => 'desc']));
}
public function testFindByLimitAndOffset() : void
{
$rows = [
['username' => 'jwage'],
['username' => 'ocramius'],
['username' => 'andreas'],
];
$this->dataSource->expects(self::once())
->method('getSourceRows')
->willReturn($rows);
self::assertEquals([
['username' => 'ocramius'],
['username' => 'andreas'],
], $this->dataSourceObjectDataRepository->findBy([], [], 2, 1));
}
public function testFindByLimit() : void
{
$rows = [
['username' => 'jwage'],
['username' => 'ocramius'],
['username' => 'andreas'],
];
$this->dataSource->expects(self::once())
->method('getSourceRows')
->willReturn($rows);
self::assertEquals([
['username' => 'jwage'],
], $this->dataSourceObjectDataRepository->findBy([], [], 1));
}
public function testFindByOffset() : void
{
$rows = [
['username' => 'jwage'],
['username' => 'ocramius'],
['username' => 'andreas'],
];
$this->dataSource->expects(self::once())
->method('getSourceRows')
->willReturn($rows);
self::assertEquals([
['username' => 'ocramius'],
['username' => 'andreas'],
], $this->dataSourceObjectDataRepository->findBy([], [], null, 1));
}
}

View File

@@ -0,0 +1,189 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSource;
use Doctrine\Website\DataSource\Sorter;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use function usort;
class SorterTest extends TestCase
{
public function testSingleAsc() : void
{
$sorter = new Sorter(['numComments' => 'asc']);
$rows = [
['numComments' => 2],
['numComments' => 1],
];
usort($rows, $sorter);
self::assertEquals([
['numComments' => 1],
['numComments' => 2],
], $rows);
}
public function testSingleDesc() : void
{
$sorter = new Sorter(['numComments' => 'desc']);
$rows = [
['numComments' => 1],
['numComments' => 2],
];
usort($rows, $sorter);
self::assertEquals([
['numComments' => 2],
['numComments' => 1],
], $rows);
}
public function testMultipleAsc() : void
{
$sorter = new Sorter(['numComments' => 'asc', 'name' => 'asc']);
$rows = [
[
'name' => 'Andreas',
'numComments' => 1,
],
[
'name' => 'Marco',
'numComments' => 2,
],
[
'name' => 'Jon',
'numComments' => 2,
],
];
usort($rows, $sorter);
self::assertEquals([
[
'name' => 'Andreas',
'numComments' => 1,
],
[
'name' => 'Jon',
'numComments' => 2,
],
[
'name' => 'Marco',
'numComments' => 2,
],
], $rows);
}
public function testMultipleDesc() : void
{
$sorter = new Sorter(['numComments' => 'desc', 'name' => 'desc']);
$rows = [
[
'name' => 'Andreas',
'numComments' => 2,
],
[
'name' => 'Marco',
'numComments' => 1,
],
[
'name' => 'Jon',
'numComments' => 1,
],
];
usort($rows, $sorter);
self::assertEquals([
[
'name' => 'Andreas',
'numComments' => 2,
],
[
'name' => 'Marco',
'numComments' => 1,
],
[
'name' => 'Jon',
'numComments' => 1,
],
], $rows);
}
public function testMultipleMixed() : void
{
$sorter = new Sorter(['numComments' => 'desc', 'name' => 'asc']);
$rows = [
[
'name' => 'Andreas',
'numComments' => 2,
],
[
'name' => 'Marco',
'numComments' => 1,
],
[
'name' => 'Jon',
'numComments' => 1,
],
];
usort($rows, $sorter);
self::assertEquals([
[
'name' => 'Andreas',
'numComments' => 2,
],
[
'name' => 'Jon',
'numComments' => 1,
],
[
'name' => 'Marco',
'numComments' => 1,
],
], $rows);
}
public function testInvalidComparisonFieldThrowsInvalidArgumentException() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unable to find comparison field username');
$sorter = new Sorter(['username' => 'asc']);
$rows = [
['email' => 'test1@example.com'],
['email' => 'test2@example.com'],
];
usort($rows, $sorter);
}
public function testInvalidOrderThrowsInvalidArgumentException() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('$order value of invalid is not accepted. Only a value of asc or desc is allowed.');
$sorter = new Sorter(['username' => 'invalid']);
}
public function testEmptyOrderByThrowsInvalidArgumentException() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The Sorter class does not accept an empty $orderBy');
$sorter = new Sorter([]);
}
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSources;
use DateTimeImmutable;
use Doctrine\Website\Builder\SourceFile;
use Doctrine\Website\Builder\SourceFileParameters;
use Doctrine\Website\Builder\SourceFileRepository;
use Doctrine\Website\DataSource\Sorter;
use Doctrine\Website\DataSources\BlogPosts;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use function usort;
class BlogPostsTest extends TestCase
{
/** @var SourceFileRepository|MockObject */
private $sourceFileRepository;
/** @var BlogPosts */
private $blogPosts;
public function testGetSourceRows() : void
{
$sourceFile1 = new SourceFile(
'md',
'/tmp/test1.html',
'/tmp/test1.html',
'test1',
new SourceFileParameters([
'url' => '/2018/09/02/test1.html',
'title' => 'Test Blog Post',
'authorName' => 'Jonathan H. Wage',
'authorEmail' => 'jonwage@gmail.com',
])
);
$sourceFile2 = new SourceFile(
'md',
'/tmp/test.html',
'/tmp/test.html',
'test2',
new SourceFileParameters([
'url' => '/2018/09/01/test2.html',
'title' => 'Test Blog Post',
'authorName' => 'Jonathan H. Wage',
'authorEmail' => 'jonwage@gmail.com',
])
);
$files = [$sourceFile1, $sourceFile2];
$this->sourceFileRepository->expects(self::once())
->method('getFiles')
->with('', 'source/blog')
->willReturn($files);
$blogPostRows = $this->blogPosts->getSourceRows();
usort($blogPostRows, new Sorter(['date' => 'desc']));
$expected = [
[
'url' => '/2018/09/02/test1.html',
'title' => 'Test Blog Post',
'authorName' => 'Jonathan H. Wage',
'authorEmail' => 'jonwage@gmail.com',
'contents' => 'test1',
'date' => new DateTimeImmutable('2018-09-02'),
],
[
'url' => '/2018/09/01/test2.html',
'title' => 'Test Blog Post',
'authorName' => 'Jonathan H. Wage',
'authorEmail' => 'jonwage@gmail.com',
'contents' => 'test2',
'date' => new DateTimeImmutable('2018-09-01'),
],
];
self::assertEquals($expected, $blogPostRows);
}
protected function setUp() : void
{
$this->sourceFileRepository = $this->createMock(SourceFileRepository::class);
$this->blogPosts = new BlogPosts(
$this->sourceFileRepository
);
}
}

View File

@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSources;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
use Doctrine\Website\DataSources\Contributors;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectContributor;
use Doctrine\Website\Model\TeamMember;
use Doctrine\Website\Repositories\ProjectContributorRepository;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class ContributorsTest extends TestCase
{
/** @var ProjectContributorRepository|MockObject */
private $projectContributorRepository;
/** @var Contributors */
private $contributors;
protected function setUp() : void
{
$this->projectContributorRepository = $this->createMock(ProjectContributorRepository::class);
$this->contributors = new Contributors($this->projectContributorRepository);
}
public function testGetSourceRows() : void
{
$project1 = new Project([]);
$project2 = new Project([]);
$jwageTeamMember = new TeamMember();
$ocramiusTeamMember = new TeamMember();
$objectManager = $this->createMock(ObjectManagerInterface::class);
$projectContributor1 = new ProjectContributor();
$projectContributor1->hydrate([
'github' => 'jwage',
'teamMember' => $jwageTeamMember,
'avatarUrl' => 'https://avatars1.githubusercontent.com/u/97422?s=460&v=4',
'numCommits' => 1,
'numAdditions' => 1,
'numDeletions' => 1,
'project' => $project1,
], $objectManager);
$projectContributor2 = new ProjectContributor();
$projectContributor2->hydrate([
'github' => 'jwage',
'teamMember' => $jwageTeamMember,
'avatarUrl' => 'https://avatars1.githubusercontent.com/u/97422?s=460&v=4',
'numCommits' => 1,
'numAdditions' => 1,
'numDeletions' => 1,
'project' => $project2,
], $objectManager);
$projectContributor3 = new ProjectContributor();
$projectContributor3->hydrate([
'github' => 'ocramius',
'teamMember' => $ocramiusTeamMember,
'avatarUrl' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
'numCommits' => 1,
'numAdditions' => 1,
'numDeletions' => 1,
'project' => $project2,
], $objectManager);
$projectContributors = [$projectContributor1, $projectContributor2, $projectContributor3];
$this->projectContributorRepository->expects(self::once())
->method('findAll')
->willReturn($projectContributors);
$rows = $this->contributors->getSourceRows();
self::assertEquals([
'jwage' => [
'teamMember' => $jwageTeamMember,
'isTeamMember' => true,
'github' => 'jwage',
'avatarUrl' => 'https://avatars1.githubusercontent.com/u/97422?s=460&v=4',
'numCommits' => 2,
'numAdditions' => 2,
'numDeletions' => 2,
'projects' => [$project1, $project2],
],
'ocramius' => [
'teamMember' => $ocramiusTeamMember,
'isTeamMember' => true,
'github' => 'ocramius',
'avatarUrl' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
'numCommits' => 1,
'numAdditions' => 1,
'numDeletions' => 1,
'projects' => [$project2],
],
], $rows);
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSources;
use Doctrine\Website\DataSources\DoctrineUsers;
use PHPUnit\Framework\TestCase;
class DoctrineUsersTest extends TestCase
{
public function testGetSourceRows() : void
{
$rows = [
[
'name' => 'Symfony',
'url' => 'https://symfony.com',
],
[
'name' => 'Laravel',
'url' => 'https://laravel.com',
],
];
$doctrineUsers = new DoctrineUsers($rows);
self::assertEquals($rows, $doctrineUsers->getSourceRows());
}
}

View File

@@ -0,0 +1,252 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSources;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
use Doctrine\Website\DataSources\ProjectContributors;
use Doctrine\Website\Github\GithubProjectContributors;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\TeamMember;
use Doctrine\Website\Repositories\ProjectRepository;
use Doctrine\Website\Repositories\TeamMemberRepository;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class ProjectContributorsTest extends TestCase
{
/** @var ProjectRepository|MockObject */
private $projectRepository;
/** @var TeamMemberRepository|MockObject */
private $teamMemberRepository;
/** @var GithubProjectContributors|MockObject */
private $githubProjectContributors;
/** @var ProjectContributors */
private $projectContributors;
protected function setUp() : void
{
$this->projectRepository = $this->createMock(ProjectRepository::class);
$this->teamMemberRepository = $this->createMock(TeamMemberRepository::class);
$this->githubProjectContributors = $this->createMock(GithubProjectContributors::class);
$this->projectContributors = new ProjectContributors(
$this->projectRepository,
$this->teamMemberRepository,
$this->githubProjectContributors
);
}
public function testGetSourceRows() : void
{
$objectManager = $this->createMock(ObjectManagerInterface::class);
$project1 = new Project(['slug' => 'orm']);
$project2 = new Project(['slug' => 'dbal']);
$jwageTeamMember = $this->createMock(TeamMember::class);
$jwageTeamMember->expects(self::at(0))
->method('isProjectMaintainer')
->willReturn(true);
$jwageTeamMember->expects(self::at(1))
->method('isProjectMaintainer')
->willReturn(false);
$ocramiusTeamMember = $this->createMock(TeamMember::class);
$this->projectRepository->expects(self::once())
->method('findAll')
->willReturn([$project1, $project2]);
$this->githubProjectContributors->expects(self::at(0))
->method('getProjectContributors')
->with($project1)
->willReturn([
[
'weeks' => [
['a' => 1, 'd' => 1],
['a' => 2, 'd' => 3],
],
'author' => [
'login' => 'jwage',
'avatar_url' => 'https://avatars1.githubusercontent.com/u/97422?s=60&v=4',
],
'total' => 5,
],
[
'weeks' => [
['a' => 2, 'd' => 2],
['a' => 2, 'd' => 3],
],
'author' => [
'login' => 'ocramius',
'avatar_url' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
],
'total' => 10,
],
[
'weeks' => [
['a' => 2, 'd' => 2],
['a' => 2, 'd' => 3],
],
'author' => [
'login' => 'bob',
'avatar_url' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
],
'total' => 10,
],
]);
$this->githubProjectContributors->expects(self::at(1))
->method('getProjectContributors')
->with($project2)
->willReturn([
[
'weeks' => [
['a' => 1, 'd' => 1],
['a' => 2, 'd' => 3],
],
'author' => [
'login' => 'jwage',
'avatar_url' => 'https://avatars1.githubusercontent.com/u/97422?s=60&v=4',
],
'total' => 5,
],
[
'weeks' => [
['a' => 2, 'd' => 2],
['a' => 2, 'd' => 3],
],
'author' => [
'login' => 'ocramius',
'avatar_url' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
],
'total' => 10,
],
[
'weeks' => [
['a' => 2, 'd' => 2],
['a' => 2, 'd' => 3],
],
'author' => [
'login' => 'jim',
'avatar_url' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
],
'total' => 10,
],
]);
$this->teamMemberRepository->expects(self::at(0))
->method('findOneByGithub')
->with('jwage')
->willReturn($jwageTeamMember);
$this->teamMemberRepository->expects(self::at(1))
->method('findOneByGithub')
->with('ocramius')
->willReturn($ocramiusTeamMember);
$this->teamMemberRepository->expects(self::at(2))
->method('findOneByGithub')
->with('bob')
->willReturn(null);
$this->teamMemberRepository->expects(self::at(3))
->method('findOneByGithub')
->with('jwage')
->willReturn($jwageTeamMember);
$this->teamMemberRepository->expects(self::at(4))
->method('findOneByGithub')
->with('ocramius')
->willReturn($ocramiusTeamMember);
$this->teamMemberRepository->expects(self::at(5))
->method('findOneByGithub')
->with('jim')
->willReturn(null);
$rows = $this->projectContributors->getSourceRows();
self::assertEquals([
[
'teamMember' => $jwageTeamMember,
'isTeamMember' => true,
'isMaintainer' => true,
'projectSlug' => 'orm',
'project' => $project1,
'github' => 'jwage',
'avatarUrl' => 'https://avatars1.githubusercontent.com/u/97422?s=60&v=4',
'numCommits' => 5,
'numAdditions' => 3,
'numDeletions' => 4,
],
[
'teamMember' => $ocramiusTeamMember,
'isTeamMember' => true,
'isMaintainer' => false,
'projectSlug' => 'orm',
'project' => $project1,
'github' => 'ocramius',
'avatarUrl' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
'numCommits' => 10,
'numAdditions' => 4,
'numDeletions' => 5,
],
[
'teamMember' => null,
'isTeamMember' => false,
'isMaintainer' => false,
'projectSlug' => 'orm',
'project' => $project1,
'github' => 'bob',
'avatarUrl' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
'numCommits' => 10,
'numAdditions' => 4,
'numDeletions' => 5,
],
[
'teamMember' => $jwageTeamMember,
'isTeamMember' => true,
'isMaintainer' => false,
'projectSlug' => 'dbal',
'project' => $project2,
'github' => 'jwage',
'avatarUrl' => 'https://avatars1.githubusercontent.com/u/97422?s=60&v=4',
'numCommits' => 5,
'numAdditions' => 3,
'numDeletions' => 4,
],
[
'teamMember' => $ocramiusTeamMember,
'isTeamMember' => true,
'isMaintainer' => false,
'projectSlug' => 'dbal',
'project' => $project2,
'github' => 'ocramius',
'avatarUrl' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
'numCommits' => 10,
'numAdditions' => 4,
'numDeletions' => 5,
],
[
'teamMember' => null,
'isTeamMember' => false,
'isMaintainer' => false,
'projectSlug' => 'dbal',
'project' => $project2,
'github' => 'jim',
'avatarUrl' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
'numCommits' => 10,
'numAdditions' => 4,
'numDeletions' => 5,
],
], $rows);
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSources;
use Doctrine\Website\DataSources\Projects;
use Doctrine\Website\Projects\ProjectDataReader;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
class ProjectsTest extends TestCase
{
/** @var mixed[] */
private $projectsData = [
['repositoryName' => 'doctrine2'],
['repositoryName' => 'dbal'],
];
/** @var ProjectDataReader|MockObject */
private $projectDataReader;
/** @var Projects */
private $projects;
protected function setUp() : void
{
$this->projectDataReader = $this->createMock(ProjectDataReader::class);
$this->projects = new Projects($this->projectDataReader, $this->projectsData);
}
public function testGetSourceRows() : void
{
$this->projectDataReader->expects(self::at(0))
->method('read')
->with('doctrine2')
->willReturn([
'name' => 'Object Relational Mapper',
'repositoryName' => 'doctrine2',
]);
$this->projectDataReader->expects(self::at(1))
->method('read')
->with('dbal')
->willReturn([
'name' => 'Database Abstraction Layer',
'repositoryName' => 'dbal',
]);
$projectRows = $this->projects->getSourceRows();
self::assertSame([
[
'active' => true,
'archived' => false,
'hasDocs' => true,
'integration' => false,
'name' => 'Object Relational Mapper',
'repositoryName' => 'doctrine2',
],
[
'active' => true,
'archived' => false,
'hasDocs' => true,
'integration' => false,
'name' => 'Database Abstraction Layer',
'repositoryName' => 'dbal',
],
], $projectRows);
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSources;
use Doctrine\Website\DataSource\Sorter;
use Doctrine\Website\DataSources\SitemapPages;
use Doctrine\Website\Tests\TestCase;
use function date;
use function usort;
class SitemapPagesTest extends TestCase
{
/** @var SitemapPages */
private $sitemapPages;
public function testGetSourceRows() : void
{
$sitemapPageRows = $this->sitemapPages->getSourceRows();
usort($sitemapPageRows, new Sorter(['url' => 'asc']));
self::assertCount(5, $sitemapPageRows);
self::assertSame(date('Y-m-d'), $sitemapPageRows[0]['date']->format('Y-m-d'));
self::assertSame('/', $sitemapPageRows[0]['url']);
self::assertSame('/api/inflector.html', $sitemapPageRows[1]['url']);
self::assertSame('/api/orm.html', $sitemapPageRows[2]['url']);
self::assertSame('/projects/doctrine-inflector.html', $sitemapPageRows[3]['url']);
self::assertSame('/projects/doctrine-orm.html', $sitemapPageRows[4]['url']);
}
protected function setUp() : void
{
$this->sitemapPages = new SitemapPages(__DIR__ . '/../source');
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSources;
use Doctrine\Website\DataSources\TeamMembers;
use Doctrine\Website\Tests\TestCase;
class TeamMembersTest extends TestCase
{
/** @var TeamMembers */
private $teamMembers;
protected function setUp() : void
{
$this->teamMembers = new TeamMembers([
['name' => 'ocramius'],
['name' => 'jwage'],
['name' => 'romanb'],
]);
}
public function testGetSourceRows() : void
{
$teamMemberRows = $this->teamMembers->getSourceRows();
self::assertSame([
['name' => 'ocramius'],
['name' => 'jwage'],
['name' => 'romanb'],
], $teamMemberRows);
}
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Docs;
use Doctrine\Website\Docs\APIBuilder;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\ProcessFactory;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;

View File

@@ -10,16 +10,20 @@ use Doctrine\Website\Docs\RST\RSTBuilder;
use Doctrine\Website\Docs\RST\RSTLanguage;
use Doctrine\Website\Docs\RST\RSTLanguagesDetector;
use Doctrine\Website\Docs\SearchIndexer;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Projects\ProjectDataRepository;
use Doctrine\Website\Projects\ProjectGitSyncer;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Repositories\ProjectRepository;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\Console\Output\OutputInterface;
class BuildDocsTest extends TestCase
{
/** @var ProjectDataRepository|MockObject */
private $projectDataRepository;
/** @var ProjectRepository|MockObject */
private $projectRepository;
@@ -43,14 +47,16 @@ class BuildDocsTest extends TestCase
protected function setUp() : void
{
$this->projectRepository = $this->createMock(ProjectRepository::class);
$this->projectGitSyncer = $this->createMock(ProjectGitSyncer::class);
$this->apiBuilder = $this->createMock(APIBuilder::class);
$this->rstLanguagesDetector = $this->createMock(RSTLanguagesDetector::class);
$this->rstBuilder = $this->createMock(RSTBuilder::class);
$this->searchIndexer = $this->createMock(SearchIndexer::class);
$this->projectDataRepository = $this->createMock(ProjectDataRepository::class);
$this->projectRepository = $this->createMock(ProjectRepository::class);
$this->projectGitSyncer = $this->createMock(ProjectGitSyncer::class);
$this->apiBuilder = $this->createMock(APIBuilder::class);
$this->rstLanguagesDetector = $this->createMock(RSTLanguagesDetector::class);
$this->rstBuilder = $this->createMock(RSTBuilder::class);
$this->searchIndexer = $this->createMock(SearchIndexer::class);
$this->buildDocs = new BuildDocs(
$this->projectDataRepository,
$this->projectRepository,
$this->projectGitSyncer,
$this->apiBuilder,

View File

@@ -11,8 +11,8 @@ use Doctrine\Website\Docs\RST\RSTCopier;
use Doctrine\Website\Docs\RST\RSTFileRepository;
use Doctrine\Website\Docs\RST\RSTLanguage;
use Doctrine\Website\Docs\RST\RSTPostBuildProcessor;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\Filesystem\Filesystem;

View File

@@ -12,8 +12,8 @@ use Doctrine\RST\HTML\Nodes\ParagraphNode;
use Doctrine\RST\HTML\Nodes\TitleNode;
use Doctrine\RST\Nodes\RawNode;
use Doctrine\Website\Docs\SearchIndexer;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;

View File

@@ -4,7 +4,8 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Repositories\ProjectRepository;
use SimpleXMLElement;
use Symfony\Component\DomCrawler\Crawler;
use function explode;
@@ -104,7 +105,8 @@ class FunctionalTest extends TestCase
self::assertValid('/contribute/website/index.html');
self::assertValid('/community/index.html');
self::assertValid('/blog/index.html');
self::assertValid('/team/index.html');
self::assertValid('/team/maintainers.html');
self::assertValid('/team/contributors.html');
self::assertValid('/2018/04/06/new-website.html');
self::assertValid('/projects.html');
@@ -113,7 +115,10 @@ class FunctionalTest extends TestCase
/** @var ProjectRepository $projectRepository */
$projectRepository = $container->get(ProjectRepository::class);
foreach ($projectRepository->findAll() as $project) {
/** @var Project[] $projects */
$projects = $projectRepository->findAll();
foreach ($projects as $project) {
// project homepage
$crawler = self::assertValid(sprintf(
'/projects/%s.html',

View File

@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\Github;
use Doctrine\Common\Cache\FilesystemCache;
use Doctrine\Website\Github\GithubProjectContributors;
use Doctrine\Website\Model\Project;
use Github\Api\Repo;
use Github\Client;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class GithubProjectContributorsTest extends TestCase
{
/** @var FilesystemCache|MockObject */
private $filesystemCache;
/** @var Client|MockObject */
private $githubClient;
/** @var GithubProjectContributors */
private $githubProjectContributors;
protected function setUp() : void
{
$this->filesystemCache = $this->createMock(FilesystemCache::class);
$this->githubClient = $this->createMock(Client::class);
$this->githubProjectContributors = new GithubProjectContributors(
$this->filesystemCache,
$this->githubClient
);
}
public function testGetProjectContributors() : void
{
$id = 'doctrine-orm-contributors-data';
$expected = [['author' => ['login' => 'jwage']]];
$project = $this->createMock(Project::class);
$project->expects(self::once())
->method('getSlug')
->willReturn('orm');
$project->expects(self::once())
->method('getRepositoryName')
->willReturn('doctrine2');
$this->filesystemCache->expects(self::once())
->method('contains')
->with($id)
->willReturn(false);
$repo = $this->createMock(Repo::class);
$this->githubClient->expects(self::once())
->method('api')
->with('repo')
->willReturn($repo);
$repo->expects(self::once())
->method('statistics')
->with('doctrine', 'doctrine2')
->willReturn($expected);
$this->filesystemCache->expects(self::once())
->method('save')
->with($id, $expected, 86400);
$projectContributors = $this->githubProjectContributors->getProjectContributors($project);
self::assertEquals($expected, $projectContributors);
}
public function testGetProjectContributorsCache() : void
{
$id = 'doctrine-orm-contributors-data';
$expected = [['author' => ['login' => 'jwage']]];
$project = $this->createMock(Project::class);
$project->expects(self::once())
->method('getSlug')
->willReturn('orm');
$this->filesystemCache->expects(self::once())
->method('contains')
->with($id)
->willReturn(true);
$this->filesystemCache->expects(self::once())
->method('fetch')
->with($id)
->willReturn($expected);
$projectContributors = $this->githubProjectContributors->getProjectContributors($project);
self::assertEquals($expected, $projectContributors);
}
}

View File

@@ -2,10 +2,10 @@
declare(strict_types=1);
namespace Doctrine\Website\Tests\Blog;
namespace Doctrine\Website\Tests\Model;
use DateTimeImmutable;
use Doctrine\Website\Blog\BlogPost;
use Doctrine\Website\Model\BlogPost;
use Doctrine\Website\Tests\TestCase;
class BlogPostTest extends TestCase

View File

@@ -2,10 +2,10 @@
declare(strict_types=1);
namespace Doctrine\Website\Tests\Sitemap;
namespace Doctrine\Website\Tests\Model;
use DateTimeImmutable;
use Doctrine\Website\Sitemap\SitemapPage;
use Doctrine\Website\Model\SitemapPage;
use Doctrine\Website\Tests\TestCase;
class SitemapPageTest extends TestCase

View File

@@ -39,6 +39,7 @@ class ProjectDataReaderTest extends TestCase
'description' => 'Test description',
'keywords' => ['keyword1', 'keyword2'],
'shortName' => 'test',
'docsSlug' => 'test-project',
], $this->projectDataReader->read('test-project'));
}
@@ -61,6 +62,7 @@ class ProjectDataReaderTest extends TestCase
],
],
],
'docsSlug' => 'no-project-json',
], $this->projectDataReader->read('no-project-json'));
}
@@ -93,7 +95,7 @@ class ProjectDataReaderTest extends TestCase
[
[
'repositoryName' => 'test-integration-project',
'isIntegration' => true,
'integration' => true,
'integrationType' => 'symfony',
],
],

View File

@@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\Projects;
use Doctrine\Website\Projects\ProjectDataReader;
use Doctrine\Website\Projects\ProjectFactory;
use Doctrine\Website\Tests\TestCase;
class ProjectFactoryTest extends TestCase
{
/** @var ProjectDataReader */
private $projectDataReader;
/** @var ProjectFactory */
private $projectFactory;
protected function setUp() : void
{
$this->projectDataReader = new ProjectDataReader(__DIR__ . '/../test-projects', [], []);
$this->projectFactory = new ProjectFactory($this->projectDataReader);
}
public function testCreate() : void
{
$project = $this->projectFactory->create('no-project-json');
self::assertSame('no-project-json', $project->getRepositoryName());
}
public function testCreateWithDoctrineProjectJson() : void
{
$project = $this->projectFactory->create('test-project');
self::assertSame('test-project', $project->getRepositoryName());
self::assertSame('test', $project->getShortName());
self::assertSame('doctrine/test-project', $project->getComposerPackageName());
self::assertSame('Test description', $project->getDescription());
self::assertSame(['keyword1', 'keyword2'], $project->getKeywords());
}
}

View File

@@ -4,10 +4,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Projects;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\ProcessFactory;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectGitSyncer;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use function sprintf;

View File

@@ -1,78 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\Projects;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectFactory;
use Doctrine\Website\Projects\ProjectRepository;
use Doctrine\Website\Tests\TestCase;
class ProjectRepositoryTest extends TestCase
{
/** @var mixed[] */
private $projectsData = [
['repositoryName' => 'doctrine2'],
['repositoryName' => 'dbal'],
];
/** @var ProjectFactory */
private $projectFactory;
/** @var ProjectRepository */
private $projectRepository;
protected function setUp() : void
{
$this->projectFactory = $this->createMock(ProjectFactory::class);
$project1 = new Project([
'name' => 'ORM',
'slug' => 'orm',
'docsSlug' => 'doctrine-orm',
]);
$project2 = new Project([
'name' => 'DBAL',
'slug' => 'dbal',
'docsSlug' => 'doctrine-dbal',
]);
$this->projectFactory->expects(self::at(0))
->method('create')
->with('doctrine2')
->willReturn($project1);
$this->projectFactory->expects(self::at(1))
->method('create')
->with('dbal')
->willReturn($project2);
$this->projectRepository = new ProjectRepository($this->projectsData, $this->projectFactory);
}
public function testFindOneBySlug() : void
{
$orm = $this->projectRepository->findOneBySlug('orm');
self::assertSame('ORM', $orm->getName());
$orm = $this->projectRepository->findOneBySlug('doctrine-orm');
self::assertSame('ORM', $orm->getName());
$dbal = $this->projectRepository->findOneBySlug('dbal');
self::assertSame('DBAL', $dbal->getName());
$dbal = $this->projectRepository->findOneBySlug('doctrine-dbal');
self::assertSame('DBAL', $dbal->getName());
}
public function testFindAll() : void
{
self::assertCount(2, $this->projectRepository->findAll());
}
}

View File

@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Projects;
use Doctrine\Website\Projects\Project;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
use InvalidArgumentException;
use function assert;

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Projects;
use Doctrine\Website\Projects\ProjectVersion;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
class ProjectVersionTest extends TestCase

View File

@@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
namespace Doctrine\Website\Tests\Sitemap;
use Doctrine\Website\Sitemap\SitemapPageRepository;
use Doctrine\Website\Tests\TestCase;
use function date;
class SitemapPageRepositoryTest extends TestCase
{
/** @var SitemapPageRepository */
private $sitemapPageRepository;
public function testFindAll() : void
{
$sitemapPages = $this->sitemapPageRepository->findAll();
self::assertCount(5, $sitemapPages);
self::assertSame(date('Y-m-d'), $sitemapPages[0]->getDate()->format('Y-m-d'));
self::assertSame('/', $sitemapPages[0]->getUrl());
self::assertSame('/projects/doctrine-inflector.html', $sitemapPages[1]->getUrl());
self::assertSame('/projects/doctrine-orm.html', $sitemapPages[2]->getUrl());
self::assertSame('/api/inflector.html', $sitemapPages[3]->getUrl());
self::assertSame('/api/orm.html', $sitemapPages[4]->getUrl());
}
protected function setUp() : void
{
$this->sitemapPageRepository = new SitemapPageRepository(__DIR__ . '/../source');
}
}

Some files were not shown because too many files have changed in this diff Show More