So my friend and me were discussing on whether to use object or id in DQL
I searched on internet but I couldn't find anything in doctrine documentation to see which is the best practice
So the problem is like this
Imagine you have the Category object
You want to get the products that have the price less than 10 so for better performance you make a DQL in the product repository and pass the $category object
My friend says we should pass $category->getId() and search by Id like this
public function findCheapProducts($categoryId){
return $this->createQueryBuilder('p')
->where('p.category = :categoryId')
->andWhere('p.price < :price')
->setParameters(array(
'categoryId' => $categoryId,
'price' => 10
));
}
But what I say is like this
public function findCheapProducts(Category $category){
return $this->createQueryBuilder('p')
->where('p.category = :categoryId')
->andWhere('p.price < :price')
->setParameters(array(
'categoryId' => $category,
'price' => 10
));
}
I wrote the code here so the error or things doesn't really matter to me here
The objective is to see which is the better way to put a condition by the identifier
I'm mixing both depending on the user input. When I already have the object I pass it, if I only have the ID like after a form submission I pass the ID as it would be nonsense to load the model just to pass it to into the querybuilder. (ofc. you need a proper form validation in this case to avoid wrong IDs)
Related
I have an array of Objects that I want to store in Redis. I can break up the array part and store them as objects but I am not getting how I can get somethings like
{0} : {"foo" :"bar", "qux" : "doe"}, {1} : {"name" "Saras", "age" : 23}
and then search the db based on name and get the requested key back. I need something like this. but can't come close to getting it right.
incr id //correct
(integer) 3
get id //correct
"3"
SADD id {"name" : "Saras"} //wrong
SADD myset {"name" : "Saras"} //correct
(integer) 1
First is getting this part right.
Second is somehow getting the key from the value i.e.
if name==="Saras"
then key=1
Which I find tough. Or I can store it directly as array of objects and use a simple for loop.
for (var i = 0; i < userCache.users.length; i++) {
if (userCache.users[i].userId == userId && userCache.users[i].deviceId == deviceId) {
return i;
}
}
Kindly suggest which route is best with some implementation?
The thing I found working was storing the key as a unique identifier and stringifying the whole object while storing the data and applying JSON.parse while extracting it.
Example code:
client
.setAsync(obj.deviceId.toString(), JSON.stringify(obj))
.then((doc) => {
return client.getAsync(obj.deviceId.toString());
})
.then((doc) => {
return JSON.parse(doc);
}).catch((err) => {
return err;
});
Though stringifying and then parsing it back is a computationally heavy operation and will block the Node.js server if the size of JSON becomes large. I am probably ready to take a hit for lesser complexity because I know my JSON wouldn't be huge, but that needs to be kept in mind while going for this approach.
Redis is pretty simple key-value storage. Yes, there are other data structures like sets, but it has VERY limited query capabilities. For example, if you want to get find data by name, then you would have to to something like that:
SET Name "serialized data of object"
SET Name2 "serialized data of object2"
SET Name3 "serialized data of object3"
then:
GET Name
would return data.
Of course this means that you can't store two entries with the same names.
You can do limited text matching on keys using: http://redis.io/commands/scan
To summarize: I think you should use other tool for complex queries.
The first issue you have, SADD id {"name" : "Saras"} //wrong, is obvious since the "id" key is not of type set, it is a string type.
In redis the only access point to data is through its key.
As kiss said, perhaps you should be looking for other tools.
I went through all API documentation of Yii 2.0 to find a way to reverse back to relation class name from a model attribute.
let us suppose that class Customer has a relation
$this->hasOne(Country::className(), ['id' => 'countryId']);
and in a controller function the parameter was the attribute "countryId". How is it possible to detect the class name for the related model
Get the name of the class by removing Id from the end of the variable and capitalize it. But I cannot image any situation where this would be a normal development practice. You can also define am array to make this translation for the model.
You can try to use http://php.net/manual/en/intro.reflection.php to get the names of all the functions and try to guess the name of the relation / model based on the name of the field. If you name your classes and relation fields in a proper name then you should be able to try to again guess the model.
This still feels like a hack, create a function that returns the name of the model based on the field... easiest solution. I know you try to be lazy but this is a hacky way of programming.
I'm not very clear on what data you have to start with here. If you only have a column countryId I am not sure. But say you have the relation name 'country' and the following code in your Customer model:
public function getCountry()
{
return $this->hasOne(Country::className(), ['id' => 'countryId']);
}
This is what I would do:
$relationName = 'country';
$customer = new Customer;
$relation = $customer->getRelation($relationName);
$relationModelClass = $relation->modelClass;
You could look at \yii\db\ActiveQuery::joinWithRelations() for how they do it.
I've using Kohana for a couple of weeks. One thing I noticed is that Kohana is missing eager loading (as far as I know). Let's say I have the following tables.
Subjects
id
name
Chapters
id
subject_id
name
Videos
id
chapter_id
name
When a user opens a subject page, I want to display all the chapters and videos. With ORM, I can do
$tutorials = ORM::factory('subject')->where('id','=', 1)->find();
foreach($tutorials as $tutorial)
{
$chapters = $tutorial->chapters->find_all();
foreach($chapters as $chapter)
{
$videos = $chapter->videos->find_all();
}
}
The above code is not efficient since it makes too many queries.
I thought about using join or database query builder, but both of them do not return a model object as their results. I also looked into with(), but it seems like it only works with one-to-one relationship.
using join on an ORM object returns an OPM object, but it doesn't return the data from the joining tables.
What would be my best option here? I would like to minimize # of queries and also want to get ORM objects a result. Whatever it would be, should return all the columns from tutorials, chapters, and videos.
First of all, your code is excess. ORM method find() returns 1 Model_Subject object. See
$chapters = ORM::factory('subject', 1)->chapters->find_all();
foreach($chapters as $chapter)
{
$videos = $chapter->videos->find_all();
}
With DB builder you can make just 2 requests. First get array of all chapters ids:
$chapters = DB::select('id')
->from('chapters')
->where('subject_id', '=', '1')
->execute()
->as_array(NULL, 'id');
Second - get all videos by ids as Model_Video object
$videos = DB::select('id')
->from('videos')
->where('chapter_id', 'IN', $chapters)
->as_object('Model_Video')
->execute()
->as_array();
So I guess you want something like this.
$videos = ORM::factory('Video')
->join(array('chapters', 'chapter'), 'LEFT')->on('video.chapter_id', '=', 'chapter.id')
->join(array('subjects', 'subject'), 'LEFT')->on('chapter.subject_id', '=', 'subject.id')
->where('subject.id', '=', $id)
->find_all();
Come to think of it, if the video belongs_to chapter belongs_to subject, try the following using with():
$videos = ORM::factory('Video')
->with('chapter:subject') // These are the names of the relationships. `:` is separator
// equals $video->chapter->subject;
->where('subject.id', '=', $id)
->find_all();
With things like this it often helps to think 'backwards'. You need the videos on that subject so start with the videos instead of the subject. :)
EDIT: The drawback of the second function is that it is going to preload all the data, it might be shorter to write but heavier on the server. I'd use the first one unless I need to know the subject and chapter anyway.
I've an entity with assigned string Id on NHibernate and I've a little problem when get an entity by Id.
Example...
Suppose that have a database record like this...
Id Description
-------------------
AAA MyDescription
now, if I use "Get" method using search id "aaa"...
MYENTITYTYPE entity = Session.Get<MYENTITYTYPE>("aaa")
return right entity but Id field (entity.Id) is "aaa", while I wish it were equal to "AAA".
In summary I would like that "Get" method return the id identical to the one stored in the database...with the same case.
Is possible? How can I do?
Interesting question. My guess is that it's not possible, because the Id might exist before the DB call. Consider the following:
var foo = session.Load<Foo>("aaa"); //no DB call, foo is a proxy
Console.WriteLine(foo.Id); //Prints "aaa";
var bar = foo.Bar; //Forces loading
Console.WriteLine(foo.Id); //No matter what, the Id can't change at this point
This illustrates another reason why primary keys with meaning are usually a bad idea, especially if their input is not controlled.
Now, if instead of Get you use a query, you will get the right-cased Id:
//example with LINQ; you can use HQL, Criteria, etc
var foo = session.Query<Foo>().Single(x => x.Id == "aaa");
The drawback is that you will always go to the DB, even if the entity is already loaded.
Now, if you defined your entity as {Id, Code, Description}, where Id is a synthetic POID (I recommend Hilo or Guid) and Code is the existing string Id, you will avoid potential bugs caused by using Get instead of a query with the code.
# student.rb
has_and_belongs_to_many :courses
# course.rb
has_and_belongs_to_many :students
I'm trying to create a scope in the students model that will check if they are enrolled in a course.
The best I've come up with is:
scope :unenrolled, where(Student.courses.count => 0)
But then I get the error message
undefined method `courses'
Anybody offer any suggestions?
Alright then. So here's your code:
scope :unenrolled, where(Student.courses.count => 0)
The first problem here is the thing that's causing the error: You're calling the instance method courses on the class Student. As the name implies, you can only call an instance method on an instance of a class, not on the class itself. For example:
jim = Student.find(123)
jims_courses = jim.courses
But here's the kicker: When you call scope you're in the class context, i.e. the code isn't inside an instance method, so it gets called when your model is first declared. There's no instance at that time so you can't just call courses like you would from within one of Student's instance methods.
But that's kind of moot since you've slightly misunderstood how where works. The argument(s) you give to where are supposed to be conditions that correspond to what you would put after WHERE in an SQL query. For example where(:eye_color => 'brown') will be turned into an SQL WHERE clause like WHERE eye_color = 'brown'. :eye_color => 'brown' is just a Hash with the key :eye_color whose value is 'brown'. Calling a function on the left side of => doesn't make sense unless the function returns the name of a column/attribute in your model that ActiveRecord will understand.
So now let's figure out what you should do. If you were writing an SQL query it would look something like this:
SELECT `students`.*, COUNT(`courses_students`.*) AS `courses_count`
FROM `students`
JOIN `courses_students` ON `students`.`id` = `courses_students`.`student_id`
WHERE `courses_count` = '0'
GROUP BY `courses_students`.`student_id`;
This translates roughly to an ActiveRecord query like this:
Student.joins(:courses). // AR automatically joins courses though courses_students
select('students., COUNT(courses.) AS courses_count').
where('courses_count = 0').
group('id')
And you can plunk that directly into your scope:
scope :unenrolled, joins(:courses).
select('students.*, COUNT(courses.*) AS courses_count').
where('courses_count = 0').
group('courses.course_id')
Note: These queries are a bit off-the-cuff and may require a bit of tweaking. The easiest way to build complicated ActiveRecord queries is by entering them directly into the Rails console until you get the results you want.
Hope that's helpful!