Doctrine querybuilder result - symfony

I am developing an application using Symfony2 and Doctrine2. I also use Doctrine's QueryBuilder. I have this query:
public function getInterpDesberdinak($MarkId)
{
$qb = $this->createQueryBuilder('c')
->select('DISTINCT c.Gordailua, c')
->where('c.MarkIdGordailua = :MarkId')
->setParameter('MarkId', $MarkId);
$Emaitza = $qb->getQuery()->getResult();
return $Emaitza;
}
I would like to how the result would I get in $Emaitza would look. Would it be something like:
$Emaitza[0]['Gordailua'] = the first Gordailua value selected.
and then $Emaitza[0][?????] = The first c type object.
I am kind of confused.Thanks.

Can't remember how I came to this post, but I even read it and thought, well .. anyways ..
I think he's asking to get the first result, if so .. the query should first know what the maximum size of result is by doing
$qb->setMaxResults(1)
and then you can call a method that really defines itself
$single_result = $qb->getSingleResult()
So in the example given the code will look like
$qb->getQuery()->setMaxResults(1)->getSingleResult();
And of courses, as stated below, if the query returns null, an error will be thrown, so here a version which verifies the content of the query
if($query = $db->getQuery())
return $query->setMaxResults(1)->getSingleResult();

First of all you need to specify the From clause:
$qb->from('YourBundle\Entity\ParentOfGordailua as pg');
You can use the: getArrayResult() to fetch the data as an array, if you want get First object from the Object ArrayCollection(when you're using the getResult() you must:
$Gordailua = $Emaitza->first()->getGordailua();
Look at the reference and exaples: http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/dql-doctrine-query-language.html

Related

Simpler way to get advanced meta data in ilUIHookPluginGUI?

I am currently coding a plugin for ILIAS. The plugin itself is not at all complex but it contains several issues whereas I think we could make it simpler as it is.
The situation is following: We have a global advanced meta data field added in the user defined meta data section with a bijective identifier. The field is activated at a repository objected named course. We have manipulated the GUI with the plugin based on ilUIHookPluginGUI.
The code for this is ... well ... see it for yourself.
First of all we save the ID of the new meta data field in the settings at the ConfigGUI for the plugin:
$field_settings = new ilSetting("foo");
$field_id_value = $field_settings->set("field_id",$_POST["field_id"]);
In our class which extends ilUIHookPluginGUI we are loading the setting as following and we have the ID of the field:
$field_settings = new ilSetting("foo");
$field_id_value = $field_settings->get("field_id");
Now the fun part. With this ID and the ref_id of the object (well, we also load the object to get the ObjId) we can load the value of the meta data field setted at the course:
$object = \ilObjectFactory::getInstanceByRefId($_GET[ 'ref_id' ]);
$obj_id = $object->getId();
$result = $DIC->database()->query("SELECT value FROM adv_md_values_text WHERE obj_id = '".$obj_id."' AND field_id = '".$field_id_value."'");
$value = $DIC->database()->fetchAssoc($result);
$is_active = $value['value'];
The question is ... is there an easier way to achieve my result?
Best,
Laura
Nice question. First of all, note that I consider the advanced metadata service in ILIAS to be lacking a good readme making clear, which hooks the interface is offering for tasks such as yours. Some time ago, I had to deal with this service as well and run into similar issues. Hopefully, your question helps to document this a little better an I myself am looking forward to other suggestions, knowing that mine is not really good as well. If you have any resources, helping pushing the introduction of good readme for services and also pushing services towards using the repository pattern with a clear interface would be highly appreciated.
Concering your question of what can be improved: I see three main issues in the lines of code:
Storing an ID in the config of your plugin. Your plugin will unconfigurable for non-technical people. However, also for you this will be error prone, think about exporting-importing stuff from a test-installation to production.
Access the value by query instead of the service.
Using new and static functions inside your code making it untestable.
Step 1
Lets start with the first one. Note, that I did not manage to solve this one without introducing a new one (a new query). Bad I know. I hope that there is a better solution, I did not find one after quick research. You store the id, since the field title is not securely unique, right? This is correct, however, you could think about storing the tripplet of field_title, record_title and (maybe) scope. Note that you maybe do not need the scope since you want to use this globally. A function return you and array containing field_id and record_id could look like so:
function getFieldAndRecordIdByFieldTitles($field_title, $record_title, $scope_title){
$query = "select field.field_id,field.record_id from adv_mdf_definition as field
INNER JOIN adv_md_record as record ON record.record_id = field.record_id
INNER JOIN adv_md_record_scope as scope ON scope.record_id = field.record_id
INNER JOIN object_reference as ref ON scope.ref_id = ref.ref_id
INNER JOIN object_data as scope_data ON ref.obj_id = scope_data.obj_id
WHERE field.title='$field_title' AND record.title='$record_title' AND scope_data.title = '$scope_title'";
$set = $this->dic()->database()->query($query);
if($row = $this->dic()->database()->fetchAssoc($set))
{
return array_values($row);
}
}
Then get your values like so:
list($field_id,$record_id) = getFieldAndRecordIdByFieldTitles("my_field", "my_record", "my_scope");
Note that I am aware that I am introducing a new query here. Sorry, was the best I could come up with. I am sure there you find a better solution, if your research a bit, let us know if successful. However, we will remove one in the next step.
Step 2
Use the undocumented service, the get your value out of the advance meta data. Since you now have the record id and the field id, you can to that like so:
$record_values = new ilAdvancedMDValues($record_id, $obj_id);
$record_values->read();
$ADTGroup = $ilAdvancedMDValues->getADTGroup();
$ADT = $ilADTGroup->getElement($field_id);
$value = $ADT->getText();
/**if you have text, others are possible, such as:
switch (true) {
case ($ADT instanceof ilADTText):
break;
case ($ADT instanceof ilADTDate):
$value = $ADT->getDate();
break;
case ($ADT instanceof ilADTExternalLink):
$... = $ADT->getUrl();
$... = $ADT->getTitle();
break;
case ($ADT instanceof ilADTInternalLink):
$... = $ADT->setTargetRefId($value);
}
**/
Note that ADT's are also undocumented. There might be a better way, to get a value out of this.
Step 3
Wrap your statics and new into some injectable dependency. I usually use the bloated constructor pattern to do this. Looks like so:
public function __construct(InjectedSettings $mySettings = null)
{
if (!$mySettings) //Case in the default scenario
{
$this->mySettings = new InjectedSettings();
} else //used e.g. for unit tests, where you can stuff the constructor with a mock
{
$this->mySettings = $mySettings;
}
$this->mySettings->doSometing();
}
Note that this is not real dep. injection, still you still use new, but I think a very workable fix to use dep. injection at least for the test context in ilias.
Does this help? I hope there will be other (better answers as well).

Shortcuting Doctrine Code

Is there is any way to Shortcut this Code
$questions = $this->getdoctrine()->getrepository('AppBundle:TicketQuestions')->find($id = 1);
$question1 = $questions->getQuestions();
$option1 = $questions->getOption1();
$option2 = $questions->getOption2();
$option3 = $questions->getOption3();
Because i want to use more than 20 times.
i use this code to get some questions and options from database for a Ticket system and i hope there is a way to not write a very long code for that.
It's hard to say without seeing your Entities. For example, do you have 20 hardcoded "options" in your "Questions" entity?
If you were to use something like an array of options, you could just use a foreach loop and get all those options. But, that also has some foundation in what your data model looks like.
If you want to use an underlying datastructure (such as an array or Doctrine ArrayCollection) you'll first have to structure your database and data scheme with the correct architecture.
This is a much deeper question than can you just write a shortcut, I don't think your underlying data and architecture are implemented correctly to use data structures (array, ArrayCollections).
Thanks any way guys, i solved it like this -> i wrote new function
public function callQuestions($id){
$questions = $this->getDoctrine()->getRepository('AppBundle:TicketQuestions')->find($id);
$question = $questions->getQuestions();
$option1 = $questions->getOption1();
$option2 = $questions->getOption2();
$option3 = $questions->getOption3();
return array($question,$option1,$option2,$option3);
}
and now i can call it easily like this:
$firstQuestion = TicketsController::callQuestions(1);
i will try also to make it shorter but this one works good now.

Getting Error "No best type found for implicitly-typed array" using LINQ and Entity Framework

var allCategories = _db.Categories;
var result = from c in allCategories
select new[] { c.CategoryID, c.Name, c.SortOrder};
when i use select new {...}; but i get result as object array.
But when i try to use select new[] {...}; i get the following error.
No best type found for implicitly-typed array
Below is my complete Method of Controller.
public ActionResult Index(jQueryDataTableParamModel param=null)
{
if (Request.IsAjaxRequest() && param!=null)
{
var allCategories = _db.Categories;
var result = from c in allCategories
select new[] { c.CategoryID, c.Name, c.SortOrder};
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = allCategories.Count(),
iTotalDisplayRecords = allCategories.Count(),
aaData = result
},
JsonRequestBehavior.AllowGet);
}
return View();
}
If i am doing wrong way please guide me to right path as i am new to ASP.NET MVC.
Update:
I am getting JSON Array like this:
{"sEcho":"1","iTotalRecords":3,"iTotalDisplayRecords":3,"aaData":[{"CategoryID":1,"Name":"Computers","SortOrder":1},{"CategoryID":2,"Name":"Laptops","SortOrder":1},{"CategoryID":3,"Name":"Mobiles","SortOrder":1}]}
where as i want Json Array like this
{"sEcho":"1","iTotalRecords":3,"iTotalDisplayRecords":3,"aaData":[["CategoryID":1,"Name":"Computers","SortOrder":1],["CategoryID":2,"Name":"Laptops","SortOrder":1],["CategoryID":3,"Name":"Mobiles","SortOrder":1]]}
The reason behind this is datatables is not showing data in grid, so i guess this is the reason behind not showing data. As i am getting the array like the second one in PHP and datatables works fine over there.
Update: 2
I Just Tried like this,
select new [] { Convert.ToString(c.CategoryID), c.Name, Convert.ToString(c.SortOrder)};
as everything will become string. but now after this error is gone i am not getting the error relating to converting of string.
LINQ to Entities does not recognize the method 'System.String
ToString(Int32)' method, and this method cannot be translated into a
store expression.
What you want is not valid JSON. You could create it using string concatenation, but it would not be possible to parse it as JSON.
If you want to produce something that is possible to parse as JSON you need to follow the syntax rules.
If you want a collection of keys and values, you use an object:
{"CategoryID":1,"Name":"Computers","SortOrder":1}
If you use an array you only have values, no keys:
[1,"Computers",1]
Ok. After many tries i finally got the result somehow in arrays rather then in objects.
Many Thanks to #Guffa Also as He helped Alot in Fixing my Problem.
He Finally gave the reply
new object[] { c.CategoryID.ToString(), c.Name, c.SortOrder.ToString() }
Which should have solved my problem but for some reason asp.net LINQ is not supporting .ToString() functions and i did got this error.
The array type 'System.Object[]' cannot be initialized in a query result. Consider using 'System.Collections.Generic.List1[System.Object]' instead.
I am not good in ASP.NET MVC specially with Databases and C# so i start back to googeling.
I think i had to call query two times.
first result is converted ToList() ToList i think supports the ToString Function.
var categories = (from category in allCategories
select category).ToList();
Then here when returning Json i wrote the query back again but here i used the Categories from First Query and then .ToString was working.
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = allCategories.Count(),
iTotalDisplayRecords = allCategories.Count(),
aaData = (from category in categories
select new [] {category.CategoryID.ToString(), category.Name, category.SortOrder.ToString()}).ToArray()
},
It gave me the result i wanted but i am not sure if this is the right way. What should be the Professional way or good way to have a secure and quick response controller. As i don't want to make a mess of my Code.

Cannot implicitly convert type to List<string>

Afternoon,
I am getting the following error, and cant work out why... Can some one please take a look and let me know where i am going wrong.
Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List'
below is what i am trying to use, to get a list back so i can use it with Amazon. I have tried to remove the .ToList() bit but nothing seems to work. I am calling an MS SQL view "GetASINForUpdateLowPrices" which returns a list back of product ASIN's
List<string> prodASINs = dc.GetASINForUpdateLowPrices.ToList();
SQL for the view i am using, this may help a little bit more.
SELECT asin
FROM dbo.aboProducts
WHERE (asin NOT IN
(SELECT aboProducts_1.asin
FROM dbo.aboProducts AS aboProducts_1 INNER JOIN
dbo.LowestPrices ON aboProducts_1.asin = dbo.LowestPrices.productAsin
WHERE (dbo.LowestPrices.priceDate >= DATEADD(day, - 1, GETDATE()))))
What data type is a single ASIN?
Probably your GetASINForUpdateLowPrices is not an IEnumerable<string>. Try this to confirm:
List<string> prodASINs = dc.GetASINForUpdateLowPrices
.Select(e => e.ToString())
.ToList();
When you call your GetASINForUpdateLowPrices, it wont directly return List<string> even if there is only one field in your view. Try the following approach:
List<string> prodASINs = dc.GetASINForUpdateLowPrices
.Select(item => item.AsinFieldName)
.ToList();
Visual Studio IntelliSense should suggest you the property name after typing item.. If the property is not string try to add .ToString() at the end of the property name.
Edit: After your comment, it seems like you need to use it as .Select(item => item.asin.ToString()).
Just use var.
var prodASINs = dc.GetASINForUpdateLowPrices.ToList();
Are you sure that GetASINForUpdateLowPrices.ToList() creates a List of Strings? My best estimation is that it is a generic list of a different type.
To figure out what is going on - Change List<string> prodASINS to be Object obj. Then set a breakpoint to see what List type is actually generated by your ToList() code by checking out the object using the debugger. You can then update your code to move the values into a list of the appropriate type.
You might have to cast the right side of the assignor like this to ultimately get the job done (replacing string with another type if necessary) - List<string> prodASINs =(List<string>)dc.GetASINForUpdateLowPrices.ToList()

Returning a column from a linked table in LINQ to SQL

My problem is that I am trying to return a simple query that contains an object Story. The Story object has a UserId in the table which links to aspnet_users' UserId column. I have created a partial class for Story that adds the UserName property since it does not exist in the table itself.
The following query gets all stories; however, a pagination helper takes the query and returns only what's necessary once this is passed back to the controller.
public IQueryable<Story> FindAllStories(){
var stories = (from s in db.Stories
orderby s.DateEntered descending
select new Story
{
Title = s.Title,
StoryContent = s.StoryContent,
DateEntered = s.DateEntered,
DateUpdated = s.DateUpdated,
UserName = s.aspnet_User.UserName
}
);
return stories;
}
When the helper does a .count() on the source it bombs with the following exception:
"Explicit construction of entity type 'MyWebsite.Models.Story' in query is not allowed."
Any ideas? It's not a problem with the helper because I had this working when I simply had the UserName inside the Story table. And on a side note - any book recommendations for getting up to speed on LINQ to SQL? It's really kicking my butt. Thanks.
The problem is precisely what it tells you: you're not allowed to use new Story as the result of your query. Use an anonymous type instead (by omitting Story after new). If you still want Story, you can remap it later in LINQ to Objects:
var stories = from s in db.Stories
orderby s.DateEntered descending
select new
{
Title = s.Title,
StoryContent = s.StoryContent,
DateEntered = s.DateEntered,
DateUpdated = s.DateUpdated,
UserName = s.aspnet_User.UserName
};
stories = from s in stories.AsEnumerable() // L2O
select new Story
{
Title = s.Title,
StoryContent = s.StoryContent,
...
};
If you really need to return an IQueryable from your method and still need the Username of the user you can use DataContext.LoadOptions to eagerload your aspnet_user objects.
See this example.

Resources