If I try to remove an item from this collection
$examples = Example::where('example', '=', $data['example'])->get();
by doing
$examples->forget(20);
it doesn't remove the item from the collection, I still get back all the items that were in there originally. I have read the Laravel documentation and the api docs. And it should work (I think) but it doesn't.
Could someone point me out what I am doing wrong here?
This still returns an object.
$examples->forget(49);
return $examples->find(49);
P.S. Ever other method like push or get works.
Thanks alot!
You did a small mistake, actually you didn't notice that. I did myself :).
Forget use the array key to delete an object item from collection.
Array(0 => 'abc'
1 => 'bcd'
49 => 'aaa'
)
$examples->forget(49);
^^ array key 49
Where as, find use the id to find an object from an collection
table: examples
id example
1 abc
49 bce
$examples->find(49);
^^ `example id`
According to the documentation it says that forget() works by key. The word "key" is ambiguous because what they mean to say is array key also known as "index" and not model key which is the id.
However, in the other methods such as find() or contains() they use the word "key" to mean model key so you can see the confusion.
When you look at the source you can see that the forget() method is found in the Illuminate\Support\Collection class and not in Illuminate\Database\Eloquent\Collection.
My theory is that the support class is supposed to be more generic so it doesn't consider the model keys, but really I don't know.
I just wanted to add on to the Anam's answer. Once you have a collection you can then loop through it like this to delete by ID
function forgetById($collection,$id){
foreach($collection as $key => $item){
if($item->id == $id){
$collection->forget($key);
break;
}
}
return $collection;
}
$examples = Example::where('example', '=', $data['example'])->get();
$examples = forgetById($examples,20);
you could do
$examples->reject(20);
Like most other collection methods, reject returns a new collection instance; it does not modify the collection it is called on.
Related
I've been struggling with this for a while but can't find a clean way to do it, so I'm seeking for some help.
I have custom filters (ApiPlatform 2.5 and Symfony 5.1) on database API outputs, and I need to filter on the current workflow place, or status as you like, of each output.
The Status has the below structure, which is a symfony workflow's place :
Status = { "OPEN": 1 }
My issue is that the status is stored as an array in the DB, and I can't find a way to have the querybuilder finding a match.
I've tried to build locally an array to do an = , a LIKE or an IN :
$status['OPEN'] = 1;
$queryBuilder->andWhere(sprintf('%s.Status = :st', $rootAlias))
->leftJoin(sprintf('%s.Objs', $rootAlias), 'o')
->andWhere('o.Profile = :p')
->setParameters(array(
'st' => $status,
'p' => $profile
));
But no way :(
I implemented a workaround that works but I don't like it as I'm using workflows a lot and need a clean way to filter outputs.
My workaround is fairly simple, when the status is writen as an array in the DB, I also store it as a string in another field called StatusText, then filtering on StatusText is easy and straight.
Status can have different contents obviously : OPEN, CLOSING, CLOSED, ...
Help appreciated !!
Thanks
EDIT & Solution
As proposed by Youssef, use scienta/doctrine-json-functions and use JSON_EXTRACT :
composer require scienta/doctrine-json-functions
Important, that was part of my issue, use the Doctrine type json_array an not array to store the status or the state, however you call it, in the Database.
Integrate the alias provided inside the ApiPlatform custom filter :
$rootAlias = $queryBuilder->getRootAliases()[0];
$json_extract_string = "JSON_EXTRACT(".$rootAlias.".Status, '$.OPEN') = 1";
$queryBuilder->andwhere($json_extract_string )
->leftJoin(sprintf('%s.Objs', $rootAlias), 'o')
->andWhere('o.Profile = :p')
->setParameter('p', $profile);
You need to ask Doctrine if the JSON array contains the status, but you can't do that with the QueryBuilder method.
When you hit the ORM limitations you can use a Native Query with ResultSetMapping. It allows you to write a pure SQL query using specific features of your DBMS but still get entity objects.
Or you can use scienta/doctrine-json-functions and use JSON_EXTRACT
I'm trying to get a dataset of messages out of my firebase database and want the messages sorted by added/timestamp. But for some reason no orderby I put in the code is actually used. I tried doing these 2 things.
_messagesRef = FirebaseDatabase.instance.reference().child('messages/'+key);
_membersSubscription = _messagesRef
.orderByChild('timestamp')
.onValue//On valuechange
.listen((Event event) => _messagesSubscriptionCallback(event));
_messagesRef = FirebaseDatabase.instance.reference().child('messages/'+key);
_membersSubscription = _messagesRef
.orderByKey()
.onValue//On valuechange
.listen((Event event) => _messagesSubscriptionCallback(event));
Both give me back the same dataset that is not ordered by timestamp or key in the callback. I've added the output underneath
{
-LA-Aw6crEAV53LxdujP:{
sender:1508,
message:test s9 2,
timestamp:1523642778089
},
-LA-Arby61T1UG5URMn6:{
sender:1508,
message:test s9,
timestamp:1523642759679
},
-LA-AyC7F8KAqceZBE3j:{
sender:1509,
message:test emu 1,
timestamp:1523642786632
},
-LA22WiUfL2tbh7-OjtM:{
sender:1508,
message:Blaj,
timestamp:1523690904480
},
-LA-B29RRXbdOPT1mG7m:{
sender:1508,
message:tesy3,
timestamp:1523642806940
}
}
This is how the data should be.
I hope someone can help me with this issue. I think I might misunderstand how ordering data works with Firebase
Kind regards,
Namanix
The result you show is a JSON object with other objects in there. JSON objects are never ordered as far as I know, only retrievable by key. JSON Arrays would be, but it doesn't look like you get that. When you would change this to an array the document IDs would have to be inside the document instead of being the object key. My guess would be that 'orderBy' is meant to be used for example to limit the number of items you get for pagination. Than you can order by timestamp, limit the number of items to 20 and search from the last timestamp you got.
I think if you want to order them I would put them in a new list of objects which can be ordered.
Most likely (it's hard to be sure without seeing _messagesSubscriptionCallback) you're throwing the ordering information away when you convert the data from Firebase into a plain JSON object, which (as Rene also says) doesn't have any defined order.
But the data your request from Firebase does have ordering information, you just have to be sure to not drop it.
The usual way to do this is to listen for onChildAdded instead of onValue. With that Firebase will invoke onChildAdded for each child in turn, and it will do so in the order you requested.
Also see this example and for example what FirebaseAnimatedList does here.
I now temporarily fixed it by doing this in my callback. But this feels like a very bad way to fix it. I hope to get some thoughts on this.
static void _messagesSubscriptionCallback(Event event) {
_messagesList.clear();
_messages.clear();
_messages = event.snapshot.value;
_messagesList = _messages.keys.toList();
_messagesList.sort((a, b) {
return b.compareTo(a) ;
});
onMessagesChange();
}
Basically, I have a function that will transform an object into a different object, and it's like a dictionary, but I don't know how to type it.
var myFunctions = {
a: () => something1,
b: () => something2,
[...]
}
gets transformed into
var myObject = {
a: something1,
b: something2
[...]
}
With Flow 0.33+ you can use $ObjMap
type ExtractCodomain = <V>(v: () => V) => V;
declare function f<O>(o: O): $ObjMap<O, ExtractCodomain>;
I don't think you can do this with Flow. The closest you can get is probably this:
function<T>(obj: T): ([key: $Keys<T>]: boolean)
That function is typed to return an object with the same key as input object, but with boolean-only values (as an example, you can specify another type). Sorry to disappoint, but it's hard to type highly dynamic code with Flow in general.
Note that the $Keys feature is undocumented because it's not part of the public API, so its behavior is defined solely by its implementation (in other words, it can change anytime).
If you're interested in the details of Flow's type system, check out the typings that come with flow in its own /lib directory, for example https://github.com/facebook/flow/blob/master/lib/core.js – you'll see that some things like Object.assign are special-cased, so you might not be able to re-implement such things in your own code.
Also, check out http://sitr.us/2015/05/31/advanced-features-in-flow.html for other "dollar features" such as $Shape and $Diff – it's partially outdated, but can give some good pointers.
#Nikita gave you the best answer for now. That said, the use-case you talked about is being discussed in the issues on the FlowType repository. It may land soon.
As of right now, if you've got mixed type, I'll just fallback to any
function<T>(obj: T): ([key: $Keys<T>]: any)
This way, at least the key names are validated. I expect within a few more versions of Flow, this problem will get solved.
I am trying to return all documents in a collection, to use it with an {{#each}} in my template. My code looks like this:
return Answers.find({}, {sort: {$natural:-1}})
But the documents are returned in natural order (not reverse). Does anyone know why? I got the $natural selector from the MongoDB documentation, so I don't see what's wrong.
Can't tell why don't it returns in reverse order.
But you can create an array in the template helper method and return reverse of an array using array.sort() or array.reverse() functions.
For ex: Say you Answers collection looks like this:
Answers({ansNo: 1, ansBody: "body1"},
{ansNo: 2, ansBody: "body2"},
{ansNo: 3, ansBody: "body3"});
And the array to be returned is:
var AnswersArr = new Array();
then in your template helper :->
var tempCollection = Answers.find({});
tempCollection.forEach(function(data){
var obj = {ansNo: data.asnNo, ansBody: data.ansBody};
AnswersArr.push(abj);
});
AnswersArr.sort(function(a, b){return b.ansNo - a.ansNo;}); //sort in reverse order
return AnswersArr;
Sort isn't a parameter but a separate function to be called after find() on the resulting Cursor object. This is the method that the MongoDB documentation is referring to and works with drivers such as MongoJS:
return Answers.find().sort({$natural: -1});
It seems Meteor hasn't added the sort() function to their implementation of Cursor, so an alternate solution would be to sort by the _id field which is generated based on date (and hence insertion order):
return Answers.find({}, {sort: {'_id': -1}});
As a workaround you could do this:
return Answers.find().fetch().reverse();
I know it would be nicer to do it via the sort parameter, but I don't think it's possible right now.
I think you might be confusing definitions of 'natural order' here. One the one hand there is a natural sort order for letters/strings (A,B,C...) and numbers (1,2,3...).
But in the case of mongo, 'natural' refers to the order the data was written to disk. '{$natural:1}' returns 'documents in the order they exist on disk...' and, so '{$natural:-1}' reverses that (http://docs.mongodb.org/manual/reference/operator/meta/natural/).
So without the code that writes the data and some insight into how it was written to disk, we cannot test your hypothesis that it is not working correctly.
After hours of trying and searching, I think its time to share my problem with you right now.
Problem Definition :
I have a Dictionary of KeyValuePairs(named filterPool) which includes an integer (PropertyID) and a string(McValue). What I am trying to do is filtering products depending on those KeyValuePairs and return them as a DataTable/List.
You may consider this as building dynamic "Where ... And .." clauses as SQL.
Here is the code that I am using :
foreach (KeyValuePair<int, string> filter in filterPool)
{
products = products.Where(i => i.PROPERTYID == filter.Key && i.MCVALUE.Equals(filter.Value));
}
return products.ToDataTable();
The problem is the foreach loop above seems to work only once, for the latest KeyValuePair available in the Dictionary.
As far as I could find on Stackoverflow, the closest solution to my problem was : this one, also using a Dictionary of values for filtering
There must be a way to achieve the goal of filtering using Dictionary and LINQ; or there's a huge thing that I am missing/ignoring to see somehow.
Hope the problem given is clear enough for all,
Thanks
^^
This is a closure issue. You can solve it by making a temporary:
foreach (KeyValuePair<int, string> filterTmp in filterPool)
{
var filter = filterTmp; // Make a temporary
products = products.Where(i => i.PROPERTYID == filter.Key && i.MCVALUE.Equals(filter.Value));
}
return products.ToDataTable();
For details on what's happening, see Eric Lippert's post Closing over the loop variable considered harmful.
Also note that this behavior has changed for C# 5. In C# 5/VS2012, this code would work as expected.
You're overwriting your products collection on every iteration of your foreach. I'm not sure what the data type on your collection is, but you'll want to do something like this in your foreach instead:
products.AddRange(products.Where(i => i.PROPERTYID == filter.Key && i.MCVALUE.Equals(filter.Value)));
I'm not sure if that makes sense, but it seems like you're trying to create a collection full of products that match your filterPool.
I think that it's better solved with aggregate:
return filter
.Aggregate(products, (acc, filter) => acc.Where(i => i.PROPERTYID == filter.Key && i.MCVALUE.Equals(filter.Value)));
.ToDataTable();