I have used laravel 5.6 and used the updateOrCreate model to add or update some data.
But I need to get all the values which changed
$q=Userssub::updateOrCreate(
['userid' => $uid ],
['model' => $model]
);
and the result shows like in this image
How can I get the changes array?
I tried to get it with
$u->changes
and
$u->changes->toarray()
but both return null.
What can I do to get the changed values?
Eloquent models have two protected arrays, $original and $changes, which contain the attributes as they were when fetched from storage and the attrbirutes which have been modified, respectively.
So you can use getOriginal() and getChanges() and compare the differences.
$model = Model::createOrUpdate([...]);
// wasRecentlyCreated is a boolean indicating if the model was inserted during the current request lifecycle.
if (!$model->wasRecentlyCreated) {
$changes = $model->getChanges();
}
This creates an array which will contain the original attribute value and what it was changed to:
if (!$model->wasRecentlyCreated) {
$original = $model->getOriginal();
$changes = [];
foreach ($model->getChanges() as $key => $value) {
$changes[$key] = [
'original' => $original[$key],
'changes' => $value,
];
}
}
e.g.
(
[first_name] => [
[original] => Kevinn
[changes] => Kevin
]
[website] => [
[original] => google.com
[changes] => google.ca
]
)
Related
Not sure if my question is clear, but here's what I'm trying to achieve. Let’s say I have a multidimensional array like so:
$arr['client1']**['dog']['Jack']**
$arr['client2']['cat']['Stacy']
How can I get the second portion of the array (between **), knowing it can be anything. For client 3, it could be a crocodile. For Client 4, it could be a car.
So I'm looking to "build" the structure of the array, dynamically. Something like so:
$arr['client1']{partBetweenThe**InTheExemple}
{partBetweenThe**InTheExemple} would be constructed "on the fly" (hence, the dynamically).
EDIT: Hopefully some clarifications...
The array changes every time. Basically, I'm building an addon to poll any API on the web. The data structure I'm getting can be anything. So what I need to do is build the key combination "on the fly", with variables.
In the exemple above, my variable would be something like $query = ['dog']['Jack'] and to get the value, I would poll it like so (from a logistic perspective, I know this doesn't work):
$arr['client1'][$query] or $arr['client1']$query or $arr['client1']{$query}
You can define the query as an array with each level as an element. Then we can iterate through that and check if we find a matching key in the response:
function findInArray(array $query, array $data)
{
foreach ($query as $key) {
if (!array_key_exists($key, $data)) {
// The key was not found, abort and return null
return null;
}
// Since the key was found, move to next level
$data =& $data[$key];
}
return $data;
}
// Example response
$response = [
'client1' => [
'dog' => [
'Jack' => 'Some value',
],
]
];
// Define the query as an array
$query = ['dog', 'Jack'];
$result = findInArray($query, $response['client1']);
Demo: https://3v4l.org/WjXTn
Edit:
So since the array's structure can't be changed this will return the client if the structure remains ['client']['animal']['name'].
$clients = [
'client1' => [
'dog' => [
'Jack' => []
]
],
'client2' => [
'cat' => [
'Stacy' => []
]
]
];
$animal = 'dog';
$name = 'Jack';
foreach ($clients as $client => $options) {
if (
array_key_exists($animal, $options) &&
array_key_exists($name, $options[$animal])
) {
echo $client;
break;
}
}
I'm developing with DataTables in Laravel and trying to make an object manually using collect() to create a collection. When I push the collection into the DataTable, there is something wrong, and I can't call my object with this $object->attribute.
After I get the error with that, I already tried to call an attribute with $object['attribute'], and it works well.
Can someone give me insight about the differences and how I can convert $object['attribute'] into $object->attribute?
This is my query to create object
$result = collect();
$item = collect([
'row' => ($key+1),
'item_id' => $value->uid,
'item' => $value->nama_item,
'sub_kategori' => $value->sub_jenis_item->sub_jenis_item,
'kategori' => $value->jenis_item->jenis_item,
'gudang_id' => $id_gudang
]);
$result->push($item);
Accessing $object['attribute'] means $object is an array and accessing $object->attribute means $object is an object.
To convert array to object:
$object = (object) $object;
Additionally, to convert object to array:
$object = (array) $object;
DataTables calls internally toArray() on the collection items when you build the table. This happens during transformation of the data. It will also flatten nested objects (e.g. loaded Eloquent relations in case of an EloquentDataTable) into an array with depth 1 (per row of the table).
You can try the following way,
$result = collect();
$item = collect([
'row' => ($key+1),
'item_id' => $value->uid,
'item' => $value->nama_item,
'sub_kategori' => $value->sub_jenis_item->sub_jenis_item,
'kategori' => $value->jenis_item->jenis_item,
'gudang_id' => $id_gudang
]
);
$result->push($item);
$resultObj = json_decode($result);
foreach($resultObj as $obj){
echo $obj->row;
}
Given following collection/array:
[
"somename" => "test.test.be"
"anothername" => "test"
"yetanothername" => "testing"
"extrafield" => "extra",
"extrafield" => "extra",
]
When i retrieve this collection i always know the order of them, but i will not know the key-names. So what i want to do is transform this collection and change the keynames to my defined values.
For a non-associative array i would do something like
$trimmedCollection->transform(function ($item) {
return [
'email' => $item[0],
'first_name' => $item[1],
'surname' => $item[2],
];
});
But how would i handle this for the given collection? Also what to do with overflow items. Say i suddenly got 10 key-value pairs but only wrote a transform for 3 how would i transform all the overflow to a default key?
Edit:
For the overflow items i would like to assign all extra fields in the given array to be stored like so.
Below would be the final array:
[
"email" => "test.test.be"
"first_name" => "test"
"surname" => "testing"
"additional_fields" => ["key-name" => "extra","key-name" => "extra"]
]
Where the key-name is the original name of the key i retrieved.
You can use array_shift to remove the 1st element in the array for every known element, and add the remaining array to your additional_fields key:
$trimmedCollection->transform(function ($item) {
return [
'email' => array_shift($item), //$item[0]
'first_name' => array_shift($item), //$item[1]
'surname' => array_shift($item), //$item[2]
'additional_fields' => $item //all remaining items
];
});
You could do something like this to transform your selected keys. This retains the other values with their unchanged keys.
function replace_array_key(array &$item, $oldKey, $newKey)
{
$item[$newKey] = $item[$oldKey];
unset($item[$oldKey]);
}
$trimmedCollection->transform(function ($item) {
replace_array_key($item, 'somename', 'email');
replace_array_key($item, 'anothername', 'first_name');
replace_array_key($item, 'yetanothername', 'surname');
return $item;
});
You can even extend this to an array list of old and new key names and run it through and arrap_map.
I want to return the nameservers of a domain in php, however I'm only getting one nameserver put in the array.
This is the code I am using:
//get auth ns datat
$authnsData = dns_get_record($domain, DNS_NS);
//put the results into a nice array
foreach ($authnsData as $nsinfo)
{
$authns = array(
'nsdata' => array(
'nameserver' => $nsinfo['target'],
'ip' => $this->getnsIP($nsinfo['target']),
'location' => $this->getipLocation($this->getnsIP($nsinfo['target'])),
),
);
return $authns;
}
The result I'm getting is:
Array
(
[nsdata] => Array
(
[nameserver] => ns-us.1and1-dns.org
[ip] => 217.160.83.2
[location] =>
)
)
Lets say a domain has 2 or more nameservers, I'm only getting one of those added to the array.
If you would like to test it out to find out the issue, the code is in this file: https://github.com/Whoisdoma/core/blob/master/src/Whoisdoma/Controllers/DNSWhoisController.php#L38
The function is getAuthNS, and LookupAuthNS. Before anyone suggests using a for loop, I have tried a for ($num = 0;) type loop.
You're returning too early, so that your loop only ever runs a single iteration.
You're assigning a new array to $authns in every iteration, instead of pushing into it.
Try this piece of code instead:
foreach ($authnsData as $nsinfo)
{
$authns[] = [
'nsdata' => [
'nameserver' => $nsinfo['target'],
'ip' => $this->getnsIP($nsinfo['target']),
'location' => $this->getipLocation($this->getnsIP($nsinfo['target'])),
],
];
}
return $authns;
BTW, there's no need to run getnsIP twice. You can use this instead:
foreach ($authnsData as $nsinfo)
{
$nameserver = $nsinfo['target'];
$ip = $this->getnsIP($nameserver);
$location = $this->getipLocation($ip);
$authns[] = ['nsdata' => compact('nameserver', 'ip', 'location')];
}
return $authns;
is it possible to get the new/updated _id after the query?
example code:
$key = array( 'something' => 'unique' );
$data = array( '$inc' => array( 'someint' => 1 ) );
$mongodb->db->collection->update( $key, $data, array( 'upsert' => true ) );
$key is not holding the new/old _id object and i assume that $data will not either because its just an instruction.
Yes -- It is possible using a single query.
MongoDB includes a findAndModify command that can atomically modify a document and return it (by default it actually returns the document before it's been modified).
The PHP drivers don't include a convenient method for this on the collection class (yet -- check out this bug), but it can still be used (note that my PHP is terrible, so I may very well have made a syntax error in the following snippet):
$key = array( 'something' => 'unique' );
$data = array( '$inc' => array( 'someint' => 1 ) );
$result = $mongodb->db->command( array(
'findAndModify' => 'collection',
'query' => $key,
'update' => $data,
'new' => true, # To get back the document after the upsert
'upsert' => true,
'fields' => array( '_id' => 1 ) # Only return _id field
) );
$id = $result['value']['_id'];
Just in case someone stumbles across this question like I did, Mongo will actually modify the input array when you call MongoCollection->save(); - appending the id to the end.
So, if you call:
$test = array('test'=>'testing');
mongocollection->save($test);
echo $test['_id'];
You will have the mongo id for that object.
I ran into this issue and worked around it by querying back the _id after the upsert. I thought I'd add some of my findings in case they're useful to anyone who comes here searching for info.
When the upsert results in a new document being created in the collection, the returned object contains the _id (here's a print_r of an example):
Array
(
[updatedExisting] => 0
[upserted] => MongoId Object
(
[$id] => 506dc50614c11c6ebdbc39bc
)
[n] => 1
[connectionId] => 275
[fsyncFiles] => 7
[err] =>
[ok] => 1
)
You can get the _id from this:
$id = (string)$obj['upserted'];
However, if the upsert resulted in an existing document being updated then the returned object does not contain _id.
Give this a shot :
function save($data, $id = null) {
$mongo_id = new MongoId($id);
$criteria = array('_id' => $mongo_id);
// Wrap a '$set' around the passed data array for convenience
$update = array('$set' => $data);
$collection->update($criteria, $update, array('upsert' => true));
}
So lets say the passed $id is null, a fresh MongoId is created, otherwise it just converts the existing $id to a MongoId object.
Hope this helps :D
The update method returns an array with the ID of the document UPSERTED:
Array
(
[ok] => 1
[nModified] => 0
[n] => 1
[err] =>
[errmsg] =>
[upserted] => MongoId Object
(
[$id] => 5511da18c8318aa1701881dd
)
[updatedExisting] =>
)
You can also set fsync to true in an update/upsert, to get the _id returned to the object that has been passed to the update.
$save = array ('test' => 'work');
$m->$collection->update(criteria, $save, array('fsync' => true, 'upsert' => true));
echo $save['_id']; //should have your _id of the obj just updated.