Can anyone explain how CDbCriteria->scopes works? - yii

I've just checked the man page of CDbCriteria, but there is not enough info about it.
This property is available since v1.1.7 and I couldn't find any help for it.
Is it for dynamically changing Model->scopes "on-the-fly"?

Scopes are an easy way to create simple filters by default. With a scope you can sort your results by specific columns automatically, limit the results, apply conditions, etc. In the links provided by #ldg there's a big example of how cool they are:
$posts=Post::model()->published()->recently()->findAll();
Somebody is retrieving all the recently published posts in one single line. They are easier to maintain than inline conditions (for example Post::model()->findAll('status=1')) and are encapsulated inside each model, which means big transparency and ease of use.
Plus, you can create your own parameter based scopes like this:
public function last($amount)
{
$this->getDbCriteria()->mergeWith(array(
'order' => 't.create_time DESC',
'limit' => $amount,
));
return $this;
}
Adding something like this into a Model will let you choose the amount of objects you want to retrieve from the database (sorted by its create time).
By returning the object itself you allow method chaining.
Here's an example:
$last3posts=Post::model()->last(3)->findAll();
Gets the last 3 items. Of course you can expand the example to almost any property in the database. Cheers

Yes, scopes can be used to change the attributes of CDbCriteria with pre-built conditions and can also be passed parameters. Before 1.1.7 you could use them in a model() query and can be chained together. See:
http://www.yiiframework.com/doc/guide/1.1/en/database.ar#named-scopes
Since 1.1.7, you can also use scopes as a CDbCriteria property.
See: http://www.yiiframework.com/doc/guide/1.1/en/database.arr#relational-query-with-named-scopes

Related

Add row to Eloquent Collection by default based on raw query

In my Eloquent collections, I'd like to add an extra column called "editable". "Editable" should be included in each query I run on some models. "Editable" show either be true or false, based on a raw query.
So I have a query that should be runned in each query on my models. Adding an extra column to my collection. The value of "editable" is determined by a raw query.
What is the best way to do this?
You could add an addSelect() method to your query chain to include the custom attribute..
Something like
$results = YourModelClass::select("*")
->addSelect(DB::raw("IF(condition,1,0) AS editable"))
->get();
In the above case, you would replace condition with your relevant SQL statement that would be evaluated per-row as part of the query. If the statement is true, then editable = 1 and if false then editable = 0 for each row returned to your Collection.
EDIT: I just saw that you want this on every query, so you probably would need a global scope/trait for your models, but the above technique for including the extra attribute should be the correct one.
I won't copy/paste the documentation on adding global scopes, that's in the core Laravel docs and I'm sure you can find it.
You can add a custom getter to your model:
public function getEditableAttribute()
{
/* return result from your raw query here */;
}

How do I implement, for instance, "group membership" many-to-many in Parse.com REST Cloud Code?

A user can create groups
A group had to have created by a user
A user can belong to multiple groups
A group can have multiple users
I have something like the following:
Parse.Cloud.afterSave('Group', function(request) {
var creator = request.user;
var group = request.object;
var wasGroupCreated = group.existed;
if(wasGroupCreated) {
var hasCreatedRelation = creator.relation('hasCreated');
hasCreatedRelation.add(group);
var isAMemberOfRelation = creator.relation('isMemberOf');
isAMemberOfRelation.add(group);
creator.save();
}
});
Now when I GET user/me with include=isMemberOf,hasCreated, it returns me the user object but with the following:
hasCreated: {
__type: "Relation"
className: "Group"
},
isMemberOf: {
__type: "Relation"
className: "Group"
}
I'd like to have the group objects included in say, 'hasCreated' and 'isMemberOf' arrays. How do I pull that using the REST API?
More in general though, am I approaching this the right way? Thoughts? Help is much appreciated!
First off, existed is a function that returns true or false (in your case the wasGroupCreated variable is always going to be a reference to the function and will tis always evaluate to true). It probably isn't going to return what you expect anyway if you were using it correctly.
I think what you want is the isNew() function, though I would test if this works in the Parse.Cloud.afterSave() method as I haven't tried it there.
As for the second part of your question, you seem to want to use your Relations like Arrays. If you used an array instead (and the size was small enough), then you could just include the Group objects in the query (add include parameter set to isMemberOf for example in your REST query).
If you do want to stick to Relations, realise that you'll need to read up more in the documentation. In particular you'll need to query the Group object using a where expression that has a $relatedTo pointer for the user. To query in this manner, you will probably need a members property on the Group that is a relation to Users.
Something like this in your REST query might work (replace the objectId with the right User of course):
where={"$relatedTo":{"object":{"__type":"Pointer","className":"_User","objectId":"8TOXdXf3tz"},"key":"members"}}

Rails 3: Excluding Results by Default

On my site, moderators can flag spammy comments. When these comments are flagged, they get quarantined so they no longer appear in regular views, though they can still be seen in the administrative control panel. At the moment, I exclude them from regular views like so:
#comments = Comment.where(:flagged => false)
I do this in every controller that has comments in it, of which there are many. I get the feeling that there's a cleaner way to handle this in Rails. Perhaps somewhere in the comments model I can specify that when querying for comments, only retrieve those that aren't flagged. If so, how is that done? And even if that's not possible, is there some other way to dry this code?
u can use a default scope
default_scope where(:flagged => false)
see http://apidock.com/rails/ActiveRecord/Base/default_scope/class
the default scope can be ignored using unscoped. See http://apidock.com/rails/ActiveRecord/Base/unscoped/class
i would prefer using a scope rather a default scope since i dont have to override it when all the records are needed. Depends upon the frequency of fetching all/unflagged records.
Make a scope (named 'clean' for this example):
class Comment < ActiveRecord
scope :clean, where(:flagged => false)
end
Then use:
#comments = Comment.clean
For future-proofing, you may may want to add a class method called default_view which just calls clean and use that instead. As your 'default' needs change, just modify the default_view method.

Fluent nHibernate Selective loading for collections

I was just wondering whether when loading an entity which contains a collection e.g. a Post which may contain 0 -> n Comments if you can define how many comments to return.
At the moment I have this:
public IList<Post> GetNPostsWithNCommentsAndCreator(int numOfPosts, int numOfComments)
{
var posts = Session.Query<Post>().OrderByDescending(x => x.CreationDateTime)
.Take(numOfPosts)
.Fetch(z => z.Comments)
.Fetch(z => z.Creator).ToList();
ReleaseCurrentSession();
return posts;
}
Is there a way of adding a Skip and Take to Comments to allow a kind of paging functionality on the collection so you don't end up loading lots of things you don't need.
I'm aware of lazy loading but I don't really want to use it, I'm using the MVC pattern and want my object to return from the repositories loaded so I can then cache them. I don't really want my views causing select statements.
Is the only real way around this is to not perform a fetch on comments but to perform a separate Select on Comments to Order By Created Date Time and then Select the top 5 for example and then place the returned result into the Post object?
Any thoughts / links on this would be appreciated.
Thanks,
Jon
A fetch simple does a left-outer join on the associated table so that it can hydrate the collection entities with data. What you are looking to do will require a separate query on the specific entities. From there you can use any number of constructs to limit your result set (skip/take, setmaxresults, etc)

EF: How to do effective lazy-loading (not 1+N selects)?

Starting with a List of entities and needing all dependent entities through an association, is there a way to use the corresponding navigation-propertiy to load all child-entities with one db-round-trip? Ie. generate a single WHERE fkId IN (...) statement via navigation property?
More details
I've found these ways to load the children:
Keep the set of parent-entities as IQueriable<T>
Not good since the db will have to find the main set every time and join to get the requested data.
Put the parent-objects into an array or list, then get related data through navigation properties.
var children = parentArray.Select(p => p.Children).Distinct()
This is slow since it will generate a select for every main-entity.
Creates duplicate objects since each set of children is created independetly.
Put the foreign keys from the main entities into an array then filter the entire dependent-ObjectSet
var foreignKeyIds = parentArray.Select(p => p.Id).ToArray();
var children = Children.Where(d => foreignKeyIds.Contains(d.Id))
Linq then generates the desired "WHERE foreignKeyId IN (...)"-clause.
This is fast but only possible for 1:*-relations since linking-tables are mapped away.
Removes the readablity advantage of EF by using Ids after all
The navigation-properties of type EntityCollection<T> are not populated
Eager loading though the .Include()-methods, included for completeness (asking for lazy-loading)
Alledgedly joins everything included together and returns one giant flat result.
Have to decide up front which data to use
It there some way to get the simplicity of 2 with the performance of 3?
You could attach the parent object to your context and get the children when needed.
foreach (T parent in parents) {
_context.Attach(parent);
}
var children = parents.Select(p => p.Children);
Edit: for attaching multiple, just iterate.
I think finding a good answer is not possible or at least not worth the trouble. Instead a micro ORM like Dapper give the big benefit of removing the need to map between sql-columns and object-properties and does it without the need to create a model first. Also one simply writes the desired sql instead of understanding what linq to write to have it generated. IQueryable<T> will be missed though.