Why isn't there a find_or_create_by! method with mongoid? - mongoid

Mongoid offers methods create and create!
such as
Artist.create(name: "Pablo Picasso")
or
Artist.create!(name: "Pablo Picasso")
Mongoid also offers a convenient method called find_or_create_by
such as
a = Artist.find_or_create_by(name: "Pablo Picasso")
It seems Mongoid should supply a method find_or_create_by!
to raise an exception if validation fails and it can't create the document.
I know with Mongoid 3.1.0, you can do
Artist.where(name: "Pablo Picasso").first_or_create
or
Artist.where(name: "Pablo Picasso").first_or_create!
But aren't they equivalent to find_or_create_by and find_or_create_by! (if one exists?)?
The find_or_create_by syntax is much shorter hence nicer...

You`re right, right now there is no find_or_create_by! method. Please open a new issue https://github.com/mongoid/mongoid/issues/new and I will add that as soon as I have time.
Thanks for asking anyways.

Related

Can't make work child admin in sonata admin bundle

I have a simple relation model in my project, I have requests and comments and created admins classes for both entities. They works just fine as separate admins, and all relations works fine on a public part of the project, but I can't set up comments as a child admin class for requests.
here is what I have in services.yaml
admin.maintenanceRequest:
class: App\Admin\MaintenanceRequestAdmin
arguments: [~, App\Entity\MaintenanceRequest ,~]
tags:
- {name: sonata.admin, manager_type: orm, label: Maintenance }
public: true
calls:
- [ addChild, ['#App\Admin\MaintenanceCommentAdmin', 'maintenance_request_id']]
I also defined $parentAssociationMapping in my child admin class:
protected $parentAssociationMapping = 'maintenance_request_id';
But it leads to this error:
Cannot autowire service "App\Admin\MaintenanceCommentAdmin": argument
"$code" of method
"Sonata\AdminBundle\Admin\AbstractAdmin::__construct()" has no
type-hint, you should configure its value explicitly.
I tried to find solution on my own for quite a while, but seems like no one have problem with that. Hope someone can help me, Im really new to sonata and coding in general, any help would be greatly appreciated. I'll provide any additional info if it's required.
I just found a solution: change your
- [ addChild, ['#App\Admin\MaintenanceCommentAdmin', 'maintenance_request_id']]
with
- [ addChild, ['#admin.maintenanceComment', 'maintenance_request_id']]

Doctrine update model with oneToMany fields

Couldn't get much help from the World Wide Wide Wide Web ...
The situation is:
My model has two manyToMany fields and one oneToMany field.
When I update the model, doctrine magically remove the old values for manyToMany fields and set the new one,
but for the oneToMany field it does not remove the old value but add the new one.
Is this a normal behaviour?
To work around I am DQLing to empty the oneToMany field target Entity for the mapped by model id and persisting new values
i.e Model: Rule has
/**
* #OneToMany(targetEntity="\Tier", mappedBy="rule", cascade={"persist", "remove"})
* #JoinColumn(name="ruleId", referencedColumnName="ruleId")
* #var Tier[]
*/
public $tiers;
And in the Rule Mapper I am deleting all tiers before calling PERSIST to get UPDATE working:
public function resetTiers($ruleId)
{
$modelClass = "Tier";
$q = $this->doctrineEntityManager->createQuery("Delete from $modelClass m where m.rule =" . $ruleId);
$numDeleted = $q->execute();
return $numDeleted;
}
I don't mind doing this way as long as its OK and not introducing any bad practice.
Thank you for your time.
Please read this bit of Doctrine's documentation.
Doctrine will only check the owning side of an association for changes.
And:
OneToMany is always the inverse side of a bidirectional association.
Although:
You can pick the owning side of a many-to-many association yourself.
So yes, it's normal behavior.
My guess is your ManyToMany relations are owned by your model (i.e. the Doctrine declaration you put in your property's comment says inversedBy and not mappedBy). Which would be why they're automatically updated (as per the doc linked above).
So either you start working on the other side's entities (namely Tier), or you trick the ORM by updating the other side's entities directly through your Rule's accessors, probably something like:
public function setTiers($tiers) {
foreach ($this->tiers as $tier) {
$tier->setRule(null);
}
foreach ($tiers as $tier) {
$tier->setRule($this);
}
if (is_array($tiers)) {
//this wrapper is useful if you use syntaxes like
//$someRule->getTiers()->add($someTier); which is usual if you use
//an addTier(Tier $tier) method declared in Rule
$tiers = new \Doctrine\Common\Collections\ArrayCollection($tiers);
}
$this->tiers = $tiers;
}
For the first option, working on other side's entities, just calling $someTier->setRule($someRule) will auto-update* $someRule's $tiers.
*: auto-update should happen 1/ when you flush your mods and 2/ maybe you'll need to refresh the $someRule object (I mean the EM's refresh method).
All of this is "my guess", anyone feel free to correct, and OP feel free to give some feedback about how it went if you try this! ;)
Here is some more doc about updating related entities, especially at the bottom of the page, section 6.14.1.
Hope this helps!

ngResource resolving nested resources

What options are there there for resolving nested resources in ngResource responses?
There have been some related questions about resolving endpoints for nested resource in ngResource, but this question is about when a REST response contains a second resource nested in the collection that is being queried, especially 1-to-1 mappings where you wouldn't have e.g. pets/<id>/owner as its own resource.
Say there are two resources, Pets and Owners:
GET /pets:
[{
name: 'spark',
type: 'dog',
owner: '/owners/3/' # alternatively just '3' or the full object.
}]
As a developer, I sometimes want to query the Owner resource as a whole, sometimes I want to query the Pet resource and then I automatically want to resolve the owner attribute into a resource instance.
This is my current solution:
.factory('Pet', function ($resource, Owner) {
var Pet = $resource('/pets/:id', {id: '#id'});
Pet.prototype.getOwner = function () {
return new Owner(this.owner); // or Owner.get({id: this.owner})
}
return Pet;
})
Problems here are many. There's integrity – for one. This implementation, I believe, allows for multiple instances of the same resource. Then there's practicality. You also have additional attributes to keep track of (owner and getOwner(), instead of just owner; possibly setOwner if you want to be able to save the model).
An alternative solution could be built on transformResponse, but it would feel like a hack to include that in every resource that has a nested mapping.
I believe this is the exact reason why Martin Gontovnikas created Restangular. He didn't like having to deal with nested $resources in the main angular framework. I think his Restangular solution would fit nicely into your needs. His code is on GitHub here and he's got a nice intro video on youtube here.
Check it out. I think you'll find it does exactly what you want it to do.
Update: I ended up working on this for a bit and have started a new angular module, available on GitHub. The answer below is about the Gist I wrote originally.
There doesn't seem to be anything around there like what I have been looking for. I have started an implementation of a solution that only supports get and getList (query) operations. The remaining methods should be trivial to add since I've pretty much kept with the layout of the ngResource module. The Gist for my implementation is below.
https://gist.github.com/lyschoening/7102262
Resources can be embedded in JSON either as full objects that simply get wrapped in the correct Resource model, or as URIs, which get resolved automatically. In addition to embedded resources, the module also supports typical nested resources, either as true parent-child collections (where the resource is only accessible after selecting the parent) or as cross-referenced collection.
Yard = Resource('/yard') # resource model
Yard.$nested('trees') # embedded item or list of items
Chair = Resource('/chair')
Yard.$nested('/chair') # sub-collection without its own model
# (for many-to-many)
Tree = Resource('/tree')
# child-collection with its own model
TreeHouse = Tree.$childResource('/treehouse')
yard = Yard.get(1)
# GET /yard/1
# {
# "uri": "/yard/1",
# "trees": [
# "/tree/15", -- reference, looked-up automatically with GET
# {"uri": "/tree/16", "name": "Apple tree"}
# -- full object, resolved to Tree instance
# ]
# }
# GET /tree/16
# {"uri": "/tree/15", "name": "Pine tree"}
yard.chair.getList()
# GET /yard/1/chair
# [{"uri": "/chair/1", ...}, ..]
# -- model inferred from URI
yard.trees[0].treehouse.getList()
# GET /tree/15/treehouse
# [{"uri": "/tree/15/treehouse/1", ...}, ..]
# -- automatically resolved to TreeHouse instance

How do I rename a mongo collection in Mongoid?

I have a collection called artists, i'd like to rename it to artist_lookups. How do I do this?
With mongoid5 / mongo ruby driver 2:
# if you need to check whether foo exists
return unless Mongoid.default_client.collections.map(&:name).include?('foo')
# rename to bar
Mongoid.default_client.use(:admin).command(
renameCollection: "#{Mongoid.default_client.database.name}.foo",
to: "#{Mongoid.default_client.database.name}.bar"
)
Very simple, in mongo shell, do that:
db.artists.renameCollection( "artist_lookups" );
if you want to drop artist_lookups if it exist:
db.artists.renameCollection( "artist_lookups", true );
Some exception you can got.
10026 – Raised if the source namespace does not exist.
10027 – Raised if the target namespace exists and dropTarget is either false or unspecified.
15967 – Raised if the target namespace is an invalid collection name.
From the Mongoid Docs:
class Band
include Mongoid::Document
store_in collection: "artists", database: "music", session: "secondary"
end
Use store_in collection: "artist_lookups" in your model. This will let you store your Artist model in the artist_lookups collection.
If you want to preserve the existing data in the artists collection, and rename it, I suggest shutting down your app temporarily, renaming the collection to artist_lookups on your MongoDB server, and then restarting the app.
db.artists.renameCollection("artist_lookups")
will work for sure.

How to set up CakePHP 2.x to behave like a RESTful webservices (for using it together with JavascriptMVC)

I am trying to set up cakephp to work with the very nice javascriptMVC (http://forum.javascriptmvc.com). JavaScriptMVC requires the JSON-Output in the following format:
[{
'id': 1,
'name' : 'Justin Meyer',
'birthday': '1982-10-20'
},
{
'id': 2,
'name' : 'Brian Moschel',
'birthday': '1983-11-10'
}]
Cake would generate a deeper nested array with a prepended Class Name. I found attempts to solve the problem but theyre not for cakephp 2.x. I know that I can simply generate a new array and json_encode() it via php, but it would be nicer to include a function like this https://gist.github.com/1874366 and another one to deflatten it.
Where would be the best place to put such functions? The AppController doesnt seem to work. Should i put it in beforeRender () or beforeFilter() of the controller? Or does someone maybe even know of an existing solution/plugin for this? This would be the best for me in my current Situation, as Im pretty much pressed for time.
Ok, I'm not 100% sure I understand what you are trying to do so here's a word to the wise just in case: Cake and JMVC are both comprehensive MVC frameworks. if you are attempting to combine them as a single cohesive platform to build your application, I strongly suggest you review your approach / platform / etc.
Also -- I'm not an expert by any means in jmvc, so I'm just going to pretend that processing the response from Cake in jmvc is completely out of the question, for some odd reason. For the record, think of Cake's responses like this:
{ "Model" :
[{
'id': 1,
'name' : 'Justin Meyer',
'birthday': '1982-10-20'
},
{
'id': 2,
'name' : 'Brian Moschel',
'birthday': '1983-11-10'
}]
}
Cake has had comprehensive REST service support, since at least Cake 1.2. The lib you are interested in is HttpSocket. As for json encoding and serving response, Request Handling covers, among other things, responding to all manners of requests, content types, decoding and encoding json, etc. Finally, the built-in Set utility will almost certainly cover whatever array manipulation you need in a line or two.
The functionality you are interested in is pretty basic and hasn't changed too much. I'd bet a lot of the (reasonably simple) solutions you have already found would probably still work, maybe with a little bit of tweaking.
For pretty much any basic service endpoint, you would probably create a controller (not AppController - that is application-wide, hence you can't invoke it directly) method, considering Cake routes the controller/action into your url:
Cake consuming services from a different app would look like this:
http://cakeproject/collect/getInfo
class CollectController extends AppController {
public function getInfo($array = null) {
App::uses('HttpSocket', 'Network/Http');
$http = new HttpSocket();
$http->get('http://jmvcproject/controller/action', $array);
// ...etc.
}
Cake providing services from the same controller / action to a different app would simply be:
public function getInfo($array = null) {
$results = $this->Collect->find('all', $array);
// ...fetch the results
}
Or you could just loop over that array with foreach($this->data as $data) { ... to drop the class name. But if your data will include associated models, etc, Set is probably the most versatile and resilient solution.
Anyway, HTH

Resources