I have an object that stores another subObject in it's data attribute. The subObject in the data attribute can be called vie a magic method like this:
$object->subObject. The subObject also has a data attribute in which it's value is stored. Currently when I do $object->subObject it automatically returns the value stored in the data attribute of the subObject. So basically the whole structure looks somewhat like this:
object [
data => subObject [
data => 'value'
]
]
$object->subObject //returns 'value'
I hope that this is understandable so far. Now I want to have the ability to execute someFunc() on the subObject: $object->subObject->someFunc(). That is generally not a problem. But I also want that, if I don't call a method on the subObject at all, the value of it's data attribute is returned ('value'). Is it possible to figure out within an object/a class if a method is called on it or not and act accordingly?
This is not possible. When you access a value, you get the same result regardless of what is going to be done with that value. It can't be different depending on whether you're going to call another method on it.
Remember that
$object->subObject->someFunc();
is essentially equivalent to
$temp = $object->subObject;
$temp->someFunc();
If the $object->subObject returned 'value', the first line would set $temp = 'value', and then $temp->someFunc() wouldn't work.
I have this function that receives a "user" model by parameter , I want to collect the properties of that object, for this I do it this way, the code works for me but the editor "phpstorm" complained to me with this error and it was to know what would be the best way to do this.
Thank you
public function sendEmail(User $user)
{
$params = [
'name' => "{$user->completeName}",
'language' => "{$user->locale}",
'user_id' => "{$user->id}"
];
}
Field accessed via magic method less... (Ctrl+F1)
Inspection info: Referenced field is not found in subject class. Note: Check is not performed on objects of type "stdClass" or derived.
Thanxs,
maybe this is simpler
$params = $user->toArray();
or
$params = $user->toJson();
That's because in Laravel your model does not actually have the properties defined.
In PHP there is the concept of magic methods though ( http://php.net/manual/en/language.oop5.overloading.php#object.get ), where the __get() method allows you to basically intercept the access to an otherwise inaccessible (or non-existing) property.
This is what happens behind the scenes in Laravel. All your property accesses are "intercepted" and Laravel looks if your database contains a column which is named like the property you are trying to access (very simplified speaking).
In a Laravel context you can savely ignore this warning.
I've a set of data (~30 properties, all having their own array of values) that I want to pass around to various classes in PHP and I want to also enforce the data's array structure. Multiple classes will be expecting this structure to be consistent.
Because of these facts I can't really rely on a standard array and so I decided to pass an object around. I looked into ArrayObject and while it allows me to set/get as if the class were an array I didn't see anything about enforcing the structure.
Is there an existing standard class that can handle enforcement of it's array-like structure while still being treated as an array, e.g., basically ArrayObject + enforcement?
An example of the array structure:
$item_type_props = array(
'phone' => array('speed' => 1000, 'self_label' => false, 'support_poe' => true, 'bluetooth' => false),
'pbx' => array('acd_support' => true, 'max_conn' => 300, max_phones => 600),
'adapter' => array('fxo' => 4, 'fxs' => 0, 't1_e1_pri' => 0),
etc...
);
I know each property in the array could be it's own class and enforce it's own fields through the constructor and set/get but then I suddenly have ~30 classes that are nothing but a bunch of attributes and that seems somewhat excessive for just holding data.
Alternatively, if I'm just approaching this from the wrong mindset and missing something really, really obvious then please do point it out. I get the feeling that I am but my brain might be having a vacation.
While you could roll your own, I encourage you to use an existing validation implementation. For example, Symfony\Validator allows you to define nested structures and the requirements on each level:
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;
$validator = Validation::createValidator();
$constraint = new Assert\Collection(array(
// the keys correspond to the keys in the input array
'name' => new Assert\Collection(array(
'first_name' => new Assert\Length(array('min' => 101)),
'last_name' => new Assert\Length(array('min' => 1)),
)),
'email' => new Assert\Email(),
'simple' => new Assert\Length(array('min' => 102)),
'gender' => new Assert\Choice(array(3, 4)),
'file' => new Assert\File(),
'password' => new Assert\Length(array('min' => 60)),
));
$violations = $validator->validate($input, $constraint);
This lets you push the details of how to validate down to another (already tested) level, while letting your code focus on why it needs this data. For the Symfony case, you can use an array as the storage mechanism, and use a design that firewalls unvalidated from validated data.
One way we might do this is notationally. Pretend we have implemented a method, perhaps using Symfony's validator, to return a validated array. We can use Hungarian notation to indicate our structure has passed through validation and is "safe":
<?php
$vInput = validate($_GET); // Hungarian notation: any variable beginning with "v" is "validated" and safe to use
function foobar(array $vInput) { ... }
While this is performant, it's not great for maintenance in the long term. So, you might consider an object wrapper that allows you to leverage the type system:
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;
class ValidatedArray extends \ArrayObject {
public function construct($input = [], $flags = 0, $iterator_class = 'ArrayIterator') {
$violations = Validation::createValidator()->validate($array, $this->constraints());
// throw exception if any violations
parent::__construct($input, $flags, $iterator_class);
}
public function __offsetSet($index, $value) {
$constraints = $this->constraints()[$index]; // specific constraints to this index
$violations = Validation::createValidator()->validate($array, $constraints);
// throw exception on violations
parent::__offsetSet($index, $value);
}
public function constraints() {
return new Assert\Collection(...);
}
}
$input = new ValidatedArray($_REQUEST); // first time validation
$input['foo'] = 'bar'; // also subject to validation
You might want to make this implementation an abstract base class, with concrete descendants implementing the constraints method to provide the specific limitations on the array object itself. This provides a flexible way to get dedicated Data Transfer Objects.
Generally speaking I would argue that - unless you are passing data to another context, e.g. javascript - a PHP application should be nicely organized in PHP classes. This is simply the easiest way to enforce the structure. Your are right, this might result in quite straightforward DTO's with a bunch of getters and setters, but it will beat checking array structures for sure. In your case there also appears to be a relationship in the array, otherwise it would not make sense combining them into an array at all.
Using PHP7 you can clearly define the method signature and enforce the types, e.g.
public function setSomething(string $myValue)
{
$this->something = $myValue;
}
Same with return types:
public function myActionMethod(array $options): ActionRespsonse
{
// Do something
}
If you have more complex data types, I would recommend using Value Objects. These are nothing more but simple PHP classes that represent a more complex value. For example, a phone number:
public function setPhoneNumber(PhoneNumber $phoneNumber)
{
$this->phoneNumber = $phoneNumber;
}
Here the PhoneNumber is a Value Object which effectively is a tiny class by itself which enforces its usage:
class PhoneNumber {
private $phoneNumber;
public __construct(string $phoneNumber) {
if (strlen($phoneNumber) != 10) {
throw new \Exception('Not a valid phone number');
}
$this->phoneNumber = $phoneNumber;
}
}
This is also where the validation can tie into the answer from #bishop as you can use an existing Validator to help you out. You can find an example of a Value Object of a Phone Number here (just googled it): Example Phone Number Value Object
I do have the feeling that you might be converting your PHP data to an array for another reason? Like interacting with a database, or passing it on to a different context, such as Javascript?
In that case, once you have your DTO's and VO's neatly defined you can then consider serializing them e.g. to/from JSON. You can use Symfony libraries for that, as described here: Symfony Serializer
If you really want arrays you can also consider hydrating them to/from entities, using the library from Marco Pivetta (ocramius) who is pretty much the authority on hydration an approach extensively used in Doctrine: Ocramius Hydrator
Plenty of options, eh!?
To be honest, though, IMHO typically there would need to be a pretty good argument to pass these complex arrays around as arrays offer very little in supporting functionality. In addition, your code will very likely and very quickly become difficult to read and maintain, as at every point where there would be some kind of modification of the array, or usage of its data elements, you would need to implement all kinds of checks or run a validation as described by #bishop. I would not allow something like that to exist in our applications code bases...
So, concluding: If you have a tightly defined set of PHP Objects with well established constructor, properties, getters, setters, constructor, relationships, action methods and besides that some kind of serializing mechanism, you're in good shape. Also other developers will be 'forced' to work with the objects and provided interface (methods and VO's) thus guaranteeing a degree of maintainability and quality.
By the way, you might consider reasing Martin Fowler's thoughts about these things: Martin Fowler: DTO Martin Fowler: VO Martin Fowler: DTO2
I want this function
$entityManager
->getRepository('AppBundle:Example')
->findBy(array('id' => $id, 'active' => true));
to return an associative array of Example objects with ID as keys.
For example:
array(
'100' => 'Example object 1',
'135' => 'Example object 2'
)
I can achieve this using foreach after retrieving the data, but I'd like to know if there is a proper way to do this.
You need to set up a custom method in your Repository, and there you can pass a second parameter to createQueryBuilder(), like this:
public function findActiveObjectsIndexedById(): array
{
return $this->createQueryBuilder('o', 'o.id')
// ...
->getQuery()
->getArrayResult();
}
I just learned this from https://stackoverflow.com/a/51689187/1668200 and couldn't find any better documentation than https://www.doctrine-project.org/api/orm/latest/Doctrine/ORM/QueryBuilder.html#method_indexBy
What you need for doing what you're suggesting, would be to define your own Custom Hydrator. This allows you to define the logic on how to prepare the result after fetching it from the storage engine.
But before going that route, I think you should ask yourself: What's the use case that desperately needs introducing another layer of complexity to my application in order to get the result in a particular way directly from the database? Do you have critical performance requirements? If so, please go ahead with the custom hydrator. Otherwise, just map your results from the objects into the desired structure before returning it, encapsulate that logic into its own class and unit test it.
If you want to skip the hydration into objects step completely, simply fetch the results directly into an array with $query->getArrayResult().
Question:
Anyone know of a way to cajole the Zend\Http\Request (or perhaps it would be in an implementer of Zend\Stdlib\ParametersInterface?) into creating urls where array query arg keys don't contain the indexes.
Background:
I'm attempting to pass an array of values as a query parameter on a GET request using the Zend\Http\Request object.
...
$httpClient = new Zend\Http\Client(); // cURL adapter setup omitted
$request = new Zend\Http\Request(); // set url, set GET method omitted
$myQueryArgArray = [
'key0' => 'val0',
'key1' => ['val1', 'val2'],
];
$request->getQuery()->fromArray($myQueryArgArray);
$response = $httpClient->send($request);
...
The cURL adapter is sending the request out the door with a url that looks like this:
hostname/path?key0=val0&key1%5B0%5D=val1&key1%5B1%5D=val2
Without the encoding:
hostname/path/?key0=val0&key1[0]=val1&key1[1]=val2
However, the server I'm calling out to fails unless I do NOT pass indexes in the query string. That is, before URL-encoding, I can call my API endpoint with a url like:
hostname/path?key0=val0&key1[]=val1&key1[]=val2
The question (again :):
Anyone know of a way to cajole the Zend\Http\Request (or perhaps it would be in an implementer of Zend\Stdlib\ParametersInterface?) into creating urls where array query arg keys don't contain the indexes.
What I've tried:
I've tried wrapping my array in a Zend\Stdlib\ArrayObject:
...
$myQueryArgArray = [
'key0' => 'val0',
'key1' => new \Zend\StdLib\ArrayObject(['val1', 'val2']),
];
...
Alas, to no avail.
I know that I accomplish the goal by manually constructing my query string and handing it directly to the Zend\Http\Request object, but I'm looking for a better way than creating my own query strings.
This page seems to indicate that there isn't a standard, so I suppose that neither ZF2 nor my api endpoint are doing it wrong:
How to pass an array within a query string?
I've looked at the sources and the problem is not the Zend\Http\Request class but the Zend\Http\Client.
Look at line 843 you see a call to the http_build_query function. And on the php.net site you have a solution in the comments:
$query = http_build_query($query);
$query = preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $query);
So the cleanest solution probably would be to extend the Zend\Http\Client class and override the send method.