Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CWD
CwdDataDoctrineORMBundle
Commits
d969a6f6
Commit
d969a6f6
authored
Jan 30, 2019
by
Bernhard Schussek
Browse files
Consolidated the different pagination implementations
parent
0a675fb2
Changes
14
Expand all
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
d969a6f6
.idea
.composer-cache
vendor
.php_cs.cache
OwnedValue/OwnedValue
.php
→
CollectionEntry/CollectionEntry
.php
View file @
d969a6f6
...
...
@@ -11,7 +11,7 @@
declare
(
strict_types
=
1
);
namespace
Cwd\DataDoctrineORMBundle\
OwnedValue
;
namespace
Cwd\DataDoctrineORMBundle\
CollectionEntry
;
use
Doctrine\Common\Collections\Collection
;
use
Webmozart\Assert\Assert
;
...
...
@@ -26,17 +26,17 @@ use Webmozart\Assert\Assert;
*
* These separate relations need an ID and a reference (foreign key) back to
* the object that owns them. None of that exists in a value object. To add
* this kind of information to a value object, it is wrapped into a
OwnedValue
.
* this kind of information to a value object, it is wrapped into a
CollectionEntry
.
*
* If you want to store a collection of value objects, create a new subclass
* of
OwnedValue
and override the OWNER_CLASS and VALUE_CLASS constants with:
* of
CollectionEntry
and override the OWNER_CLASS and VALUE_CLASS constants with:
*
* * The class name of the owning class (e.g. Contact)
* * The class name of the value object (e.g. PhoneNumber)
*
* Example:
*
* class ContactPhoneNumber extends
OwnedValue
* class ContactPhoneNumber extends
CollectionEntry
* {
* protected const OWNER_CLASS = Contact::class;
*
...
...
@@ -44,7 +44,7 @@ use Webmozart\Assert\Assert;
* }
*
* In the owning class, use the methods synchronize() and synchronizeAll() to
* synchronize
OwnedValue
entities with actual value objects.
* synchronize
CollectionEntry
entities with actual value objects.
*
* Example:
*
...
...
@@ -54,7 +54,7 @@ use Webmozart\Assert\Assert;
* ContactPhoneNumber::synchronizeAll($this->phoneNumbers, $phoneNumbers, $this);
* }
*/
abstract
class
OwnedValue
abstract
class
CollectionEntry
{
/**
* The name of the class owning the value object.
...
...
@@ -147,7 +147,7 @@ abstract class OwnedValue
/**
* Extracts the value object of an owned value.
*
* @param
OwnedValue
|null $ownedValue The owned value. May be null
* @param
CollectionEntry
|null $ownedValue The owned value. May be null
*
* @return object|null The value object or null
*/
...
...
Mapping/AdditionalMetadata.php
0 → 100644
View file @
d969a6f6
<?php
/*
* This file is part of the CWD Data Doctrine ORM Bundle
*
* (c) cwd.at GmbH <office@cwd.at>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare
(
strict_types
=
1
);
namespace
Cwd\DataDoctrineORMBundle\Mapping
;
use
function
class_exists
;
use
OutOfBoundsException
;
use
Webmozart\Assert\Assert
;
class
AdditionalMetadata
{
private
$className
;
private
$joinMappings
=
[];
private
$queryMappings
=
[];
private
$ownedClasses
=
[];
private
$suggestMappings
=
[];
public
function
__construct
(
string
$className
)
{
$this
->
className
=
$className
;
}
public
function
getClassName
():
string
{
return
$this
->
className
;
}
public
function
addJoinMapping
(
string
$joinAlias
,
array
$joinMapping
):
void
{
Assert
::
notEmpty
(
$joinAlias
);
Assert
::
keyExists
(
$joinMapping
,
'field'
);
Assert
::
keyExists
(
$joinMapping
,
'targetEntity'
);
Assert
::
keyExists
(
$joinMapping
,
'targetField'
);
Assert
::
stringNotEmpty
(
$joinMapping
[
'field'
]);
Assert
::
stringNotEmpty
(
$joinMapping
[
'targetEntity'
]);
Assert
::
stringNotEmpty
(
$joinMapping
[
'targetField'
]);
$this
->
joinMappings
[
$joinAlias
]
=
$joinMapping
;
}
public
function
getJoinMapping
(
string
$joinAlias
):
array
{
if
(
!
isset
(
$this
->
joinMappings
[
$joinAlias
]))
{
throw
new
OutOfBoundsException
(
sprintf
(
'The join mapping with the alias "%s" does not exist.'
,
$joinAlias
));
}
return
$this
->
joinMappings
[
$joinAlias
];
}
public
function
hasJoinMapping
(
string
$joinAlias
):
bool
{
return
isset
(
$this
->
joinMappings
[
$joinAlias
]);
}
public
function
getJoinMappings
():
array
{
return
$this
->
joinMappings
;
}
public
function
addQueryMapping
(
string
$fieldName
,
array
$queryMapping
):
void
{
Assert
::
notEmpty
(
$fieldName
);
$queryMapping
=
array_replace
([
'type'
=>
'string'
,
'fuzzy'
=>
true
,
],
$queryMapping
);
Assert
::
oneOf
(
$queryMapping
[
'type'
],
[
'string'
,
'date'
]);
Assert
::
boolean
(
$queryMapping
[
'fuzzy'
]);
$this
->
queryMappings
[
$fieldName
]
=
$queryMapping
;
}
public
function
getQueryFields
():
array
{
return
array_keys
(
$this
->
queryMappings
);
}
public
function
getQueryMapping
(
string
$fieldName
):
array
{
if
(
!
isset
(
$this
->
queryMappings
[
$fieldName
]))
{
throw
new
OutOfBoundsException
(
sprintf
(
'The query mapping for the field "%s" does not exist.'
,
$fieldName
));
}
return
$this
->
queryMappings
[
$fieldName
];
}
public
function
getQueryMappings
():
array
{
return
$this
->
queryMappings
;
}
public
function
addOwnedClass
(
string
$className
):
void
{
Assert
::
notEmpty
(
$className
);
Assert
::
true
(
class_exists
(
$className
));
$this
->
ownedClasses
[]
=
$className
;
}
public
function
hasOwnedClass
(
string
$className
):
bool
{
return
in_array
(
$className
,
$this
->
ownedClasses
,
true
);
}
public
function
getOwnedClasses
():
array
{
return
$this
->
ownedClasses
;
}
public
function
addSuggestMapping
(
string
$fieldName
,
array
$suggestMapping
):
void
{
Assert
::
notEmpty
(
$fieldName
);
$this
->
suggestMappings
[
$fieldName
]
=
$suggestMapping
;
}
public
function
getSuggestMapping
(
string
$fieldName
):
array
{
if
(
!
isset
(
$this
->
suggestMappings
[
$fieldName
]))
{
throw
new
OutOfBoundsException
(
sprintf
(
'The suggest mapping for the field "%s" does not exist.'
,
$fieldName
));
}
return
$this
->
suggestMappings
[
$fieldName
];
}
public
function
hasSuggestMapping
(
string
$fieldName
):
bool
{
return
isset
(
$this
->
suggestMappings
[
$fieldName
]);
}
public
function
getSuggestMappings
():
array
{
return
$this
->
suggestMappings
;
}
}
Mapping/AdditionalMetadataLoader.php
0 → 100644
View file @
d969a6f6
<?php
/*
* This file is part of the CWD Data Doctrine ORM Bundle
*
* (c) cwd.at GmbH <office@cwd.at>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare
(
strict_types
=
1
);
namespace
Cwd\DataDoctrineORMBundle\Mapping
;
use
Doctrine\ORM\EntityManager
;
use
Doctrine\ORM\Mapping\ClassMetadata
;
use
function
substr
;
use
Webmozart\Assert\Assert
;
class
AdditionalMetadataLoader
{
private
$em
;
public
function
__construct
(
EntityManager
$em
)
{
$this
->
em
=
$em
;
}
public
function
loadMetadata
(
string
$className
):
AdditionalMetadata
{
$classMetadata
=
$this
->
em
->
getClassMetadata
(
$className
);
$options
=
$classMetadata
->
table
[
'options'
]
??
[];
$joinMappings
=
$options
[
'joins'
]
??
[];
$queryMappings
=
$options
[
'query'
]
??
[];
$suggestMappings
=
$options
[
'suggest'
]
??
[];
$collectionMappings
=
$options
[
'collections'
]
??
[];
$additionalMetadata
=
new
AdditionalMetadata
(
$className
);
$this
->
loadJoinMappings
(
$additionalMetadata
,
$classMetadata
,
$joinMappings
,
$collectionMappings
);
$this
->
loadQueryMappings
(
$additionalMetadata
,
$classMetadata
,
$queryMappings
);
$this
->
loadOwnedClasses
(
$additionalMetadata
,
$collectionMappings
);
$this
->
loadSuggestMappings
(
$additionalMetadata
,
$classMetadata
,
$suggestMappings
);
return
$additionalMetadata
;
}
private
function
loadJoinMappings
(
AdditionalMetadata
$additionalMetadata
,
ClassMetadata
$classMetadata
,
array
$joinMappings
,
array
$collectionMappings
):
void
{
foreach
(
$collectionMappings
as
$collectionName
=>
$collectionMapping
)
{
if
(
's'
===
substr
(
$collectionName
,
-
1
))
{
$joinAlias
=
substr
(
$collectionName
,
0
,
-
1
);
if
(
isset
(
$joinMappings
[
$joinAlias
]))
{
continue
;
}
$joinMappings
[
$joinAlias
]
=
[
'field'
=>
$collectionName
];
}
}
foreach
(
$classMetadata
->
getFieldNames
()
as
$fieldName
)
{
$fieldMapping
=
$classMetadata
->
getFieldMapping
(
$fieldName
);
$options
=
$fieldMapping
[
'options'
]
??
[];
if
(
isset
(
$options
[
'references'
])
&&
'Id'
===
substr
(
$fieldName
,
-
2
))
{
$joinAlias
=
substr
(
$fieldName
,
0
,
-
2
);
if
(
isset
(
$joinMappings
[
$joinAlias
]))
{
continue
;
}
$joinMappings
[
$joinAlias
]
=
[
'field'
=>
$fieldName
];
}
}
foreach
(
$joinMappings
as
$joinAlias
=>
$joinMapping
)
{
$this
->
loadJoinMapping
(
$additionalMetadata
,
$joinMappings
,
$joinAlias
);
}
}
private
function
loadJoinMapping
(
AdditionalMetadata
$additionalMetadata
,
array
$joinMappings
,
string
$joinAlias
):
void
{
if
(
$additionalMetadata
->
hasJoinMapping
(
$joinAlias
))
{
// Already loaded by recursion
return
;
}
$joinMapping
=
$joinMappings
[
$joinAlias
];
if
(
!
isset
(
$joinMapping
[
'field'
]))
{
throw
new
MappingException
(
sprintf
(
'Missing option "field" on join "%s" of entity "%s".'
,
$joinAlias
,
$additionalMetadata
->
getClassName
()
));
}
if
(
isset
(
$joinMapping
[
'via'
]))
{
$this
->
loadJoinMapping
(
$additionalMetadata
,
$joinMappings
,
$joinMapping
[
'via'
]);
$viaClassMetadata
=
$this
->
em
->
getClassMetadata
(
$additionalMetadata
->
getJoinMapping
(
$joinMapping
[
'via'
])[
'targetEntity'
]
);
}
else
{
$viaClassMetadata
=
$this
->
em
->
getClassMetadata
(
$additionalMetadata
->
getClassName
());
}
$fieldMapping
=
$viaClassMetadata
->
hasField
(
$joinMapping
[
'field'
])
?
$viaClassMetadata
->
getFieldMapping
(
$joinMapping
[
'field'
])
:
[];
if
(
!
isset
(
$joinMapping
[
'targetEntity'
]))
{
if
(
isset
(
$fieldMapping
[
'options'
][
'references'
][
'entity'
]))
{
$joinMapping
[
'targetEntity'
]
=
$fieldMapping
[
'options'
][
'references'
][
'entity'
];
}
elseif
(
$viaClassMetadata
->
hasAssociation
(
$joinMapping
[
'field'
]))
{
$joinMapping
[
'targetEntity'
]
=
$viaClassMetadata
->
getAssociationTargetClass
(
$joinMapping
[
'field'
]);
}
else
{
throw
new
MappingException
(
sprintf
(
'Missing option "targetEntity" on join "%s" of entity "%s" and the '
.
'field "%s" is neither an association nor is the "references" option set '
.
'in the XML metadata of "%s".'
,
$joinAlias
,
$additionalMetadata
->
getClassName
(),
$joinMapping
[
'field'
],
$viaClassMetadata
->
getName
()
));
}
}
if
(
!
isset
(
$joinMapping
[
'targetField'
]))
{
if
(
isset
(
$fieldMapping
[
'options'
][
'references'
][
'field'
]))
{
$joinMapping
[
'targetField'
]
=
$fieldMapping
[
'options'
][
'references'
][
'field'
];
}
elseif
(
$viaClassMetadata
->
hasAssociation
(
$joinMapping
[
'field'
]))
{
$associationMapping
=
$viaClassMetadata
->
getAssociationMapping
(
$joinMapping
[
'field'
]);
// TODO figure out how to support composite primary keys, once necessary
$joinMapping
[
'field'
]
=
current
(
$viaClassMetadata
->
getIdentifierFieldNames
());
$joinMapping
[
'targetField'
]
=
$associationMapping
[
'mappedBy'
];
}
else
{
throw
new
MappingException
(
sprintf
(
'Missing option "targetField" on join "%s" of entity "%s" and the '
.
'field "%s" is neither an association nor is the "references" option set '
.
'in the XML metadata of "%s".'
,
$joinAlias
,
$additionalMetadata
->
getClassName
(),
$joinMapping
[
'field'
],
$viaClassMetadata
->
getName
()
));
}
}
$additionalMetadata
->
addJoinMapping
(
$joinAlias
,
$joinMapping
);
}
private
function
loadQueryMappings
(
AdditionalMetadata
$additionalMetadata
,
ClassMetadata
$classMetadata
,
array
$queryMappings
):
void
{
foreach
(
$queryMappings
as
$fieldName
=>
$queryMapping
)
{
if
(
''
===
$queryMapping
)
{
$queryMapping
=
[];
}
$fieldMapping
=
$this
->
getFieldMapping
(
$additionalMetadata
,
$classMetadata
,
$fieldName
);
if
(
!
isset
(
$queryMapping
[
'type'
]))
{
switch
(
$fieldMapping
[
'type'
])
{
case
'local_date'
:
$queryMapping
[
'type'
]
=
'date'
;
break
;
default
:
$queryMapping
[
'type'
]
=
'string'
;
break
;
}
}
if
(
isset
(
$queryMapping
[
'fuzzy'
]))
{
Assert
::
oneOf
(
$queryMapping
[
'fuzzy'
],
[
'true'
,
'false'
]);
$queryMapping
[
'fuzzy'
]
=
'true'
===
$queryMapping
[
'fuzzy'
];
}
$additionalMetadata
->
addQueryMapping
(
$fieldName
,
$queryMapping
);
}
}
private
function
loadOwnedClasses
(
AdditionalMetadata
$additionalMetadata
,
array
$collectionMappings
):
void
{
foreach
(
$collectionMappings
as
$fieldName
=>
$collectionMapping
)
{
$additionalMetadata
->
addOwnedClass
(
$collectionMapping
[
'entryClass'
]);
}
}
private
function
loadSuggestMappings
(
AdditionalMetadata
$additionalMetadata
,
ClassMetadata
$classMetadata
,
array
$suggestMappings
):
void
{
// If there is no custom mapping and if the field "name" exists, use it
if
(
0
===
count
(
$suggestMappings
)
&&
$classMetadata
->
hasField
(
'name'
))
{
$suggestMappings
=
[
'name'
=>
[]];
}
foreach
(
$suggestMappings
as
$fieldName
=>
$suggestMapping
)
{
if
(
''
===
$suggestMapping
)
{
$suggestMapping
=
[];
}
// Validate
$this
->
getFieldMapping
(
$additionalMetadata
,
$classMetadata
,
$fieldName
);
$additionalMetadata
->
addSuggestMapping
(
$fieldName
,
$suggestMapping
);
}
}
private
function
getFieldMapping
(
AdditionalMetadata
$additionalMetadata
,
ClassMetadata
$classMetadata
,
string
$fieldName
):
array
{
$pos
=
strpos
(
$fieldName
,
'.'
);
if
(
false
!==
$pos
)
{
$portionBeforeDot
=
substr
(
$fieldName
,
0
,
$pos
);
$portionAfterDot
=
substr
(
$fieldName
,
$pos
+
1
);
if
(
$additionalMetadata
->
hasJoinMapping
(
$portionBeforeDot
))
{
$joinMapping
=
$additionalMetadata
->
getJoinMapping
(
$portionBeforeDot
);
$targetClassMetadata
=
$this
->
em
->
getClassMetadata
(
$joinMapping
[
'targetEntity'
]);
return
$targetClassMetadata
->
getFieldMapping
(
$portionAfterDot
);
}
if
(
!
$classMetadata
->
hasField
(
$fieldName
))
{
throw
new
MappingException
(
sprintf
(
'The field "%s" is neither an embedded object nor '
.
'a mapped join on entity "%s".'
,
$fieldName
,
$additionalMetadata
->
getClassName
()
));
}
}
return
$classMetadata
->
getFieldMapping
(
$fieldName
);
}
}
Mapping/Driver/XmlDriverWithCollectionSupport.php
0 → 100644
View file @
d969a6f6
<?php
/*
* This file is part of the CWD Data Doctrine ORM Bundle
*
* (c) cwd.at GmbH <office@cwd.at>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare
(
strict_types
=
1
);
namespace
Cwd\DataDoctrineORMBundle\Mapping\Driver
;
use
Doctrine\ORM\Mapping\ClassMetadata
;
use
Doctrine\ORM\Mapping\ClassMetadataFactory
;
use
Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver
;
class
XmlDriverWithCollectionSupport
extends
SimplifiedXmlDriver
{
private
$classMetadataFactory
;
public
function
setClassMetadataFactory
(
ClassMetadataFactory
$classMetadataFactory
):
void
{
$this
->
classMetadataFactory
=
$classMetadataFactory
;
}
public
function
loadMetadataForClass
(
$className
,
\
Doctrine\Common\Persistence\Mapping\ClassMetadata
$metadata
)
{
/* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */
parent
::
loadMetadataForClass
(
$className
,
$metadata
);
$options
=
$metadata
->
table
[
'options'
]
??
[];
if
(
isset
(
$options
[
'collections'
]))
{
$collectionMappings
=
$options
[
'collections'
];
foreach
(
$collectionMappings
as
$fieldName
=>
$collectionMapping
)
{
if
(
isset
(
$metadata
->
associationMappings
[
$fieldName
]))
{
continue
;
}
$metadata
->
associationMappings
[
$fieldName
]
=
array
(
'fieldName'
=>
$fieldName
,
'targetEntity'
=>
$collectionMapping
[
'entryClass'
],
'mappedBy'
=>
'owner'
,
'cascade'
=>
[
'remove'
,
'persist'
,
'refresh'
,
'merge'
,
'detach'
],
'orphanRemoval'
=>
true
,
'orderBy'
=>
[
'id'
=>
'ASC'
],
'type'
=>
ClassMetadata
::
ONE_TO_MANY
,
'inversedBy'
=>
null
,
'isOwningSide'
=>
false
,
'sourceEntity'
=>
$metadata
->
getName
(),
'fetch'
=>
ClassMetadata
::
FETCH_LAZY
,
'isCascadeRemove'
=>
true
,
'isCascadePersist'
=>
true
,
'isCascadeRefresh'
=>
true
,
'isCascadeMerge'
=>
true
,
'isCascadeDetach'
=>
true
,
);
}
}
if
(
isset
(
$options
[
'collectionEntry'
]))
{
if
(
!
isset
(
$metadata
->
fieldMappings
[
'id'
],
$metadata
->
identifier
))
{
$metadata
->
fieldMappings
[
'id'
]
=
[
'id'
=>
true
,
'fieldName'
=>
'id'
,
'type'
=>
'integer'
,
'columnName'
=>
'id'
,
];
$metadata
->
identifier
=
[
'id'
];
$metadata
->
fieldNames
[
'id'
]
=
'id'
;
$metadata
->
columnNames
[
'id'
]
=
'id'
;
$metadata
->
isIdentifierComposite
=
false
;
$metadata
->
generatorType
=
ClassMetadata
::
GENERATOR_TYPE_AUTO
;
}
if
(
!
isset
(
$metadata
->
associationMappings
[
'owner'
]))
{
$metadata
->
associationMappings
[
'owner'
]
=
[
'fieldName'
=>
'owner'
,
'targetEntity'
=>
$options
[
'collectionEntry'
][
'ownerClass'
],
'inversedBy'
=>
$options
[
'collectionEntry'
][
'ownerField'
],
'joinColumns'
=>
[
[
'name'
=>
'owner_id'
,
'referencedColumnName'
=>
'id'
,
'nullable'
=>
false
,
'onDelete'
=>
'CASCADE'
,
],
],
'type'
=>
ClassMetadata
::
MANY_TO_ONE
,
'mappedBy'
=>
null
,
'isOwningSide'
=>
true
,
'sourceEntity'
=>
$className
,
'fetch'
=>
ClassMetadata
::
FETCH_LAZY
,
'cascade'
=>
[],
'isCascadeRemove'
=>
false
,
'isCascadePersist'
=>
false
,
'isCascadeRefresh'
=>
false
,
'isCascadeMerge'
=>
false
,
'isCascadeDetach'
=>
false
,
'sourceToTargetKeyColumns'
=>
[
'owner_id'
=>
'id'
],
'joinColumnFieldNames'
=>
[
'owner_id'
=>
'owner_id'
],
'targetToSourceKeyColumns'
=>
[
'id'
=>
'owner_id'
],
'orphanRemoval'
=>
false
,
];
}
}
}
}
Mapping/MappingException.php
0 → 100644
View file @
d969a6f6
<?php
/*
* This file is part of the CWD Data Doctrine ORM Bundle
*
* (c) cwd.at GmbH <office@cwd.at>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare
(
strict_types
=
1
);
namespace
Cwd\DataDoctrineORMBundle\Mapping
;
use
RuntimeException
;
class
MappingException
extends
RuntimeException
{
}
Pagination/PageFactory.php
0 → 100644
View file @
d969a6f6
<?php
/*
* This file is part of the CWD Data Doctrine ORM Bundle
*
* (c) cwd.at GmbH <office@cwd.at>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare
(
strict_types
=
1
);
namespace
Cwd\DataDoctrineORMBundle\Pagination
;
use
Cwd\DataBundle\Pagination\Offset\Base64Offset
;
use
Cwd\DataBundle\Pagination\Offset\NumericOffset
;
use
Cwd\DataBundle\Pagination\Page\Item
;
use
Cwd\DataBundle\Pagination\Page\Page
;