Add property and value in a object array - php

im quite new in php OOP, and im using laravel has my framework of choice. Im creating a object user and create the record, the only thing i have to do is to check if one of my input fields(not required) was set, if it was set than i have to add in in the user object, but how can i add this property (mobilephone) and value in a exiting object, am i doing it right?.
code:
$user = User::create([
'email' => $userDat['email'],
'name' => $userDat['name'],
'surname' => $userDat['surname'],
]);
if (isset($userDat['mobilephone'])) {
$user->mobilephone = $userDat['mobilephone'];
}

If you absolutely have to add the property to the object, I believe you could cast it as an array, add your property (as a new array key), then cast it back as an object. The only time you run into stdClass objects (I believe) is when you cast an array as an object or when you create a new stdClass object from scratch (and of course when you json_decode() something - silly me for forgetting!).
Instead of:
$foo = new StdClass();
$foo->bar = '1234';
You'd do:
$foo = array('bar' => '1234');
$foo = (object)$foo;
Or if you already had an existing stdClass object:
$foo = (array)$foo;
$foo['bar'] = '1234';
$foo = (object)$foo;
Also as a 1 liner:
$foo = (object) array_merge( (array)$foo, array( 'bar' => '1234' ) );
or maybe you could do it this way
$user->{'mobilephone'} = $userDat['mobilephone'];

There're a few ways you can do it.
Prepare the array in advance
$data = [
'email' => $userDat['email'],
'name' => $userDat['name'],
'surname' => $userDat['surname'],
];
if ($phone = array_get($userDat, 'mobilephone')) {
$data['mobilephone'] = $phone;
}
User::create($data);
Or create the object first then save it
$user = new User([
'email' => $userDat['email'],
'name' => $userDat['name'],
'surname' => $userDat['surname'],
]);
if ($phone = array_get($userDat, 'mobilephone')) {
$user->mobilephone = $phone;
}
$user->save();
As you are currently, but with an extra query (Ill advised)
$user = User::create([
'email' => $userDat['email'],
'name' => $userDat['name'],
'surname' => $userDat['surname'],
]);
if ($phone = array_get($userDat, 'mobilephone')) {
$user->mobilephone = $phone;
$user->save();
}

how can i add this property (mobilephone) and value in a exiting object, am i doing it right?.
This strongly depends on which framework you are using. In general, you can add arbitrary properties to any object in PHP (which does not mean you always should).
In your case, you are using the Eloquent framework that's shipped with Laravel, which actually depends on dynamically assigned object attributes and are using it absolutely correctly. This is documented in-depth in the official documentation. Note that you'll probably have to call $user->save() at some point to actually save your user object (thanks to Ben for the hint).
Note that alternatively, you could assign all properties of your User instance that way:
$user = new User();
$user->email = $userDat['email'];
$user->name = $userDat['name'];
$user->surname = $userDat['surname'];
if (isset($userDat['mobilephone'])) {
$user->mobilephone = $userDat['mobilephone'];
}
$user->save();

Related

Eloquent - Compare attribute changes after save() model

I'm building an audit system in my application, and I want to compare an Eloquent Model attribute changes after save() method. Here's an example of what I need:
$person = Person::find(1); //Original data: $person->name -> 'Original name', $person->age -> 22
$person->name = 'A new name';
$person->age = 23;
$person->save();
//At this point, I need to get an array like this (only with the attributes that have changed):
[
'age' => ['old' => 22, 'new' => 23],
'name' => ['old' => 'Original name', 'new' => 'A new name']
]
I know Eloquent already has some functions like isDirty(), getDirty() and getChanges(), however this methods only return the new values, and I need to get the old and the new values to store in my audit table.
Is there any way to do this without need to "clone" my variables and then compare it to get the changes?
Before saving your model you can access the original (old) attribute values like:
$person->original.
Furthermore you can call: $person->getChanges() to get all attributes that changed.
Before the Save() function and before overwriting the variables on lines 2 and 3, you can get old data by doing this:
$oldName = $person->name;
$oldAge = $person->age;
And then, after saving, you can insert your values in an array, like this:
$values = array(
"oldName" => $oldName,
"newName" => "New Name",
"oldAge" => $oldAge,
"newAge" => "New Age",
);
So you can get values from the array by doing:
echo $values["oldName"];
echo $values["newAge"];
...
You can clone the newly retrieved model before making the changes.
Something along the line of
$person = Person::find(1);
$original_person = cone $person;
// update the person object
// ...
$person->save();
You can proceed to construct your array like so:
[
'age' => ['old' => $original_person->age, 'new' => $person->age],
'name' => ['old' => $original_person->name, 'new' => $person->name]
]
You can do this within updated() boot function in the model class
class Mymodel extends Model
{
public static function boot()
{
parent::boot();
self::updated(function ($model) {
var_dump($model->original);
var_dump($model->getChanges());
// Traverse the changed array and save with original values
});
}
}

How can I get array key => errorMessage from $violations. Symfony 4

Here is my code
$arParams = $request->all();
$validator = Validation::createValidator();
$groups = new GroupSequence(['Default', 'custom']);
$constraint = new Assert\Collection([
'name' => new Assert\Length(['min' => 2]),
'city' => new Assert\Length(['min' => 2]),
'email' => new Assert\Email(),
'phone' => new Assert\Length(['min' => 18]),
'message' => new Assert\NotNull()
]);
$violations = $validator->validate($arParams, $constraint, $groups);
If i get some errors, how can I get an array like
['name' => not enough symbols, 'email' => wrong email]?
I tried to use foreach on $violations but cant find all the methods of its elements Phpstorm sign $violation as mixed. I found only $violation->getMessage() and ->getCode()
I recommend you to read this article https://symfony.com/doc/current/validation.html
If validation fails, a non-empty list of errors (class ConstraintViolationList) is returned.
So you can get your list this way:
if ($violations->count() > 0) {
$formatedViolationList = [];
for ($i = 0; $i < $violations->count(); $i++) {
$violation = $violations->get($i);
$formatedViolationList[] = array($violation->getPropertyPath() => $violation->getMessage());
}
}
Couple explanations. We use methods from violation api count() or get a number of violations, and after in for loop we use get($i) for get every violation by index. After we use getPropertyPath() for get path (name of your property) and getMessage() for get message.

Class properties being overwritten by other instance setters

Pulling my hair out over some weird behaviour.
Essentially I've got a Class that constructs a GuzzleHttp\Client and a custom object of organization data, like so:
// Config set-up
$config = [
'ex1' => [
'api_key' => getenv('EX1_API_KEY'),
'org_id' => getenv('EX1_ORG_ID'),
],
'ex2' => [
'api_key' => getenv('EX2_API_KEY'),
'org_id' => getenv('EX2_ORG_ID'),
],
'ex3' => [
'api_key' => getenv('EX3_API_KEY'),
'org_id' => getenv('EX3_ORG_ID'),
],
];
// Initialize adapters
$ex1 = new Adapter($config['ex1']);
$ex2 = new Adapter($config['ex2']);
$ex3 = new Adapter($config['ex3']);
Which is all a-okay, until they finish their construction with $this->org = $org, which overwrites all of them with the same $org, in this line inside the constructor:
// Construct connected org
$org = Organization::get($this, $args['org_id']);
$this->org = $org;
The frustrating part in all of this is if I assign a property of that org instead of the whole thing, each item comes through unique (e.g. $this->org = $org->name).
I have a feeling this has to do with my Organization class, but I don't know where to start debugging this. Can provide more code/context on request, but the entire code-base is on GitHub.
I structured my abstract Resource class as a Singleton pattern. Because of this, I could not have more than one instance at a time (thus, Singleton) and as such was just changing the properties of the same instance each time.

How can I reach an element in an Object?

I'm trying to reach an element in an object array. But I couldn't succeed.
Let's think this as an object called $result
How can I reach maskedNumber?
Braintree_Result_Successful Object
(
[success] => 1
[_returnObjectName:private] => customer
[customer] => Braintree_Customer Object
(
[_attributes:protected] => Array
(
[creditCards] => Array
(
[0] => Braintree_CreditCard Object
(
[_attributes] => Array
(
[maskedNumber] => ***********5897
Since the _attributes property of Braintree_Customer is protected you will need to define an accessor method. The other _attributes property of Braintree_CreditCard also looks like it's supposed to be protected, so I've assumed an identical accessor should exist:
$cards = $object->customer->getAttribute('creditCards');
$number = $cards[0]->getAttribute('maskedNumber');
Accessor method to be placed in both classes:
function getAttribute($attribute) {
return $this->_attributes[$attribute];
}
Edit:
Just to improve upon my original answer a little, I would put some decent error checking in an actual accessor method.
function getAttribute($attribute) {
if (isset($this->_attributes[$attribute])) {
return $this->_attributes[$attribute];
}
return NULL;
}
You could also consider using the magic methods __get() and __set() to act as getters and setters.
I solved the problem by asking braintreepayments. They said that I could retrieve this data after I add user to braintree. But my solution is if I really needed this at the very beginning would be to take it with REGEX. For people who are looking for a great online payment company I suggest you to go with braintree
I know this is old, but this might help some.
Braintree has methods for returning the protected information that's returned from functions like Braintree_Customer::create();
$result = Braintree_Customer::create(array(
'firstName' => $_POST['first_name'],
'lastName' => $_POST['last_name'],
'email' => $_POST['email'],
'creditCard' => array(
'cardholderName' => $_POST['cardholder_name'],
'number' => $_POST['number'],
'expirationMonth' => $_POST['month'],
'expirationYear' => $_POST['year'],
'cvv' => $_POST['cvv'],
'billingAddress' => array(
'postalCode' => $_POST['postal_code']
)
)
));
var_dump($result->customer->__get('id'));
var_dump($result->customer->__get('creditCards'));
The _attributes of customer are protected, but the get function returns them.
This method does not require re-requesting data from Braintree.
try
$Result->customer->_attributes['creditCards'][0]->_attributes['maskedNumber']
with $result->customer you should get the *Braintree_Customer* Object and then in that object you should have methods to retrieve the cards as those methods are protected and cannot be accessed directly. Something like
$customer = $result->customer;
foreach($customer->getCreditCards() as $card)
{
echo $card->getMaskedNumber(); // You will need to create that method too
}
Example of getCreditCards method:
Class Braintree_Customer
{
protected $creditCards;
function getCreditCards()
{
return $creditCards;
}
...
}

Why my object type in my doctrine2 entity doesn't get updated when calling persist() & flush()?

Here is what I'm doing:
$entity = new Meta();
$obj = new stdClass();
$obj->foo = 15;
$obj->bar = 0;
$obj->bor = true;
$entity->setObject($obj);
$em->persist($entity);
$em->flush();
$entity = $entityRepository->find(1);
var_dump($entity);
returns:
object(Jo\Model\Entity)[130]
protected 'id' => int 1
protected 'user' => null
protected 'object' =>
object(stdClass)[105]
public 'foo' => int 15
public 'bar' => int 0
public 'bor' => boolean true
$entity->getObject()->bar = 9;
var_dump($entity);
returns:
object(Jo\Model\Entity)[130]
protected 'id' => int 1
protected 'user' => null
protected 'object' =>
object(stdClass)[105]
public 'foo' => int 15
public 'bar' => int 9
public 'bor' => boolean true
!
$em->persist($entity);
$em->flush();
But after flush the entity is not updated in the database.
Maybe it is because I'm setting the new object from the getObject() method and it has something to do with references or so, but I don't understand then why the second var_dump() shows the correct values in the object.
The getter is pretty simple and just consist in returning the private property.
Any ideas, I'd like to understand this behavior.
Wasn't sure about this behavior myself but I was curious, so I talked about this shortly on IRC with Jonathan Wage, one of the Doctrine devs.
According to Jon, the reason for this behavior is that when you map an object directly to a property, like you're doing, you must clone to object - in other words, you need a new instance.
When checking if your data has changed, D2 checks whether the object is the same as the old one. It basically just does a x === y type comparison, so unless you have a completely new instance it will not recognize the data as changed.

Categories