Yii2: How to join tables with Query Builder? - join

I'm using the Yii2 framework for a very basic stock software. I have two tables of a very basic stock software:
products = (id_product, name)
movements = (id_movement, quantity, date)
The products table has all the product of a supermarket.
When the supermarket buy a product, it is registered in the movements table.
I want to show a grid with the current stock in the index.php view. I am thinking to have a actionIndex function to get the total of each products.
Basically I want to do this:
public function actionIndex()
{
Get movements and products from database;
For each product {
if (movement is IN) {
add();
}
else {
subtract();
}
}
return...
}
Using Gii Code Generator, I made a CRUD for products and a CRUD for movements.
So first, I need to get the products and movements from database. Without Yii, I can make a simple JOIN query. But I have no idea how to do it using Yii. What is the best way to do it? I would like to use the query builder.
I found this question but I didn't work for me. And this documentation but I really don't understand it.

You can use innerJoin or similar function
$result= (new Query())
->select('table1.col1, table1.col2,,,, table2.co1, table2.col2 ...')
->from('products ')
->innerJoin('movements', 'product.key= movements.key');
http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html

Related

Not retrieving all objects using Doctrine 2 querybuilder

I've made a query using querybuilder. It look likes:
//Create a Querybuilder
$qb = $this->_em->createQueryBuilder();
//Foreach entity, check if it's a join and leftjoin it.
//If there's no join, make it the select
foreach($config['entities'] as $name => $entity)
{
//add new alias(ses) to select statement
$qb->addSelect($entity['alias']);
if(!isset($entity['join'])){
$qb->from($entity['path'], $entity['alias']);
} else {
$qb->leftJoin($entity['join'], $entity['alias']);
}
}
->orWhere(':test IS NULL')
->setParameter('test', 'User\Entity\Address');
//Make the query
$query = $qb->getQuery();
//Return the result
return $query->getResult();
I've made my select and left join dynamically with a config file. So people can make own joins. But when i have (for example) users and addresses. It shows only users WITH addresses. Users without addresses are not show. When i've no join, all users show up. Has somebody an idea about it? I read something like i've to set a where clause and do something like:
->orWhere(':test IS NULL')
->setParameter('test', 'User\Entity\Address');
It doesn't work. How can i have joins AND show all the users also WITHOUT addresses?
Grtz
I've a problem with my where clause. It had a "like" in it, where he wants to like the "street" (from addresses), to something. But when users didn't had a address, he can't like and so he don't show the whole user.

ASP.NET MVC help needed

Can someone explane the following code for me?
public class StoreEditorViewModel
{
public List<Ticket> TotalView { get; set; }
public StoreEditorViewModel()
{
using (MvcTicketsEntities storeDB = new MvcTicketsEntities())
{
var temp = storeDB.Tickets.Include(x => x.Genres).Include(x => x.Artists).ToList();
TotalView = temp.ToList();
}
}
}
I don't understand the Inculde(x => x.genres) *genres is another table in my database. ( i use entity Framework)
The Include is telling EF to fetch the Genres records as part of this sql request, rather than making you call twice (once for Tickets and again for the Tickets Genres).
To quote Jon Galloway in the MVC Music Store example (your code looks very similar)
"We’ll take advantage of an Entity Framework feature that allows us to indicate other related entities we want loaded as well when the Genre object is retrieved. This feature is called Query Result Shaping, and enables us to reduce the number of times we need to access the database to retrieve all of the information we need. We want to pre-fetch the Albums for Genre we retrieve, so we’ll update our query to include from Genres.Include(“Albums”) to indicate that we want related albums as well. This is more efficient, since it will retrieve both our Genre and Album data in a single database request."

Query across multiple domain objects in Grails

I am building a "Recent Activity" page just to give some background. Basically this page will be a mash up of several different actions performed by the user. The problem is that this data spans across multiple (3 or so) domain object types all with different fields. All of the domains are associated with the User object but the associations differ.
The domains are UserPost, FriendRequest, and UserTask. Is there a way I can query all these domains, support pagination, and order by dateCreated? Would it be better to change my structure? Any help would be great!
I have implemented similar feature for my web site and I come to the conclusion that it is better to add an Activity domain class that records all kind of activities.
Let's say
class Activity {
Date dateCreated
String linkClassName
Long linkId
def getLink() { getClass().classLoader.loadClass(linkClassName).get(linkId) }
}
Then whenever you insert a new record in your UserPost, FriendRequest, and UserTask (or whatever), you create a new entry in Activity as well using afterInsert GORM events as following:
def afterInsert = {
new Activity(linkClassName: this.class.name, linkId: this.id).save()
}
Once you have done this, you can retrieve your recent activities like this:
def activities = Activity.list(max: 5, sort:'dateCreated', order:'desc')

Creating custom model using other models in ASP.NET MVC

As the title suggests I have two models Products And Orders which are actually two table in my database and I have used a LINQ to SQL class to create there models. Now I wan to create a model named "OrderDetails" which will have properties from both the model like product name and id from product and Order number from orders something like this. An then I want to create a view from this custom model and from which I want to add "CRUD" operation. What should be my approach. And in many scenarios I may have to use data from more than 4 models. Please help. I'm wondering helplessly and I have only two days experience in ASP.NET MVC.
I'd go this route:
namespace Models
{
public class OrderDetails
{
private Products _products;
private Order _order;
public OrderDetails(Order order, Products products)
{
_order = order;
_products = products;
}
// now expose whatever fields or logic you need.
// for instance:
public Customer OrderCustomer()
{
return order.Customer();
}
}
}
However, before you go around making all of these new models, are you sure you need them? Can you simply use two instances, either passed around separately or in a Tuple? You can get some useful abstraction here, but if you don't need it, keep it simple.
Linq to SQL does not support custom mapping to the extent that you're looking to do. What you'll want to look at is a full ORM. I highly recommend nHibernate, but Entity Framework, if you must, will do what you want.

Silverlight, DataPager, RIA Services, and smart paging

I'm still trying to get my feet on the ground with Silverlight and RIA Services, and of course starting with some of the more "fun" stuff like grids and intelligent paging. I can connect to RIA Services (using a home-grown ORM, not L2S or EF), get data on the grid, and connect to a DataPager. The domain service is working well with the home-grown ORM, at least for queries. (Still working on full CRUD.) However, there are still problems:
To support the user application, I need user-controlled sorting and filtering, in addition to smart paging (only run the query for the rows needed to display) and grouping.
So far, I've seen nothing in the DataGrid or DataPager to externalize these capabilities so that filtering, sorting, and paging parameters can be passed to the server to build the appropriate query.
The datasets are potentially quite large; my table I've chosen for prototyping work can have up to 35,000 entries at some customers, and I'm sure there are other tables far larger that I will have to deal with at some point. So the "smart paging" aspect is essential.
Ideas, suggestions, guidance, and nerf bricks are all welcome.
OK, I've spent a few days in the weeds with this one, and I think I've got a handle on it.
First, an important piece of magic. For paging to work properly, the pager has to know the total item count, no matter how many items were returned by the current query. If the query returns everything, the item count is obviously the number of items returned. For smart paging, the item count is still the total of available items, although the query returns only what gets displayed. With filtering, even the total of available items changes every time the filter changes.
The Silverlight Datapager control has a property called ItemCount. It is readonly and cannot be databound in XAML, or set directly in code. However, if the user control containing the pager has a DataContext that implements IPagedCollectionView, then the data context object must implement an ItemCount property with PropertyChanged notification, and the DataPager seems to pick this up automagically.
Second, I highly recommend Brad Abrams' excellent series of blog posts on RIA Services, especially this one on ViewModel. It contains most of what you need to make paging and filtering work, although it's missing the critical piece on managing the item count. His downloadable sample also contains a very good basic framework for implementing ModelViewViewModel (MVVM). Thank you, Brad!
So here's how to make the item count work. (This code refers to a custom ORM, while Brad's code uses Entity Framework; between the two you can figure you what you need in your environment.)
First, your ORM needs to support getting record counts, with and without your filter. Here's my domain service code that makes the counts available to RIA Services:
[Invoke]
public int GetExamCount()
{
return Context.Exams.Count();
}
[Invoke]
public int GetFilteredExamCount(string descriptionFilter)
{
return Context.Exams.GetFilteredCount(descriptionFilter);
}
Note the [Invoke] attribute. You need this for any DomainService method that doesn't return an Entity or an Entity collection.
Now for the ViewModel code. You need an ItemCount, of course. (This is from Brad's example.)
int itemCount;
public int ItemCount
{
get { return itemCount; }
set
{
if (itemCount != value)
{
itemCount = value;
RaisePropertyChanged(ItemCountChangedEventArgs);
}
}
}
Your LoadData method will run the query to get the current set of rows for display in the DataGrid. (This doesn't implement custom sorting yet, but that's an easy addition.)
EntityQuery<ExamEntity> query =
DomainContext.GetPagedExamsQuery(PageSize * PageIndex, PageSize, DescriptionFilterText);
DomainContext.Load(query, OnExamsLoaded, null);
The callback method then runs the query to get the counts. If no filter is being used, we get the count for all rows; if there's a filter, then we get the count for filtered rows.
private void OnExamsLoaded(LoadOperation<ExamEntity> loadOperation)
{
if (loadOperation.Error != null)
{
//raise an event...
ErrorRaising(this, new ErrorEventArgs(loadOperation.Error));
}
else
{
Exams.MoveCurrentToFirst();
if (string.IsNullOrEmpty(DescriptionFilterText))
{
DomainContext.GetExamCount(OnCountCompleted, null);
}
else
{
DomainContext.GetFilteredExamCount(DescriptionFilterText, OnCountCompleted, null);
}
IsLoading = false;
}
}
There's also a callback method for counts:
void OnCountCompleted(InvokeOperation<int> op)
{
ItemCount = op.Value;
TotalItemCount = op.Value;
}
With the ItemCount set, the Datapager control picks it up, and we have paging with filtering and a smart query that returns only the records to be displayed!
LINQ makes the query easy with .Skip() and .Take(). Doing this with raw ADO.NET is harder. I learned how to do this by taking apart a LINQ-generated query.
SELECT * FROM
(select ROW_NUMBER() OVER (ORDER BY Description) as rownum, *
FROM Exams as T0 WHERE T0.Description LIKE #description ) as T1
WHERE T1.rownum between #first AND #last ORDER BY rownum
The clause "select ROW_NUMBER() OVER (ORDER BY Description) as rownum" is the interesting part, because not many people use "OVER" yet. This clause sorts the table on Description before assigning row numbers, and the filter is also applied before row numbers are assigned. This allows the outer SELECT to filter on row numbers, after sorting and filtering.
So there it is, smart paging with filtering, in RIA Services and Silverlight!
Here's the quick and dirty solution (that I went for):
Just move your DomainDataSource to your ViewModel! Done!
May not exactly be great for testability and probably some other limitations I haven't discovered yet, but personally I don't care about that until something better comes along.
Inside your ViewModel just instantiate the data source :
// Feedback DataSource
_dsFeedback = new DomainDataSource();
_dsFeedback.DomainContext = _razorSiteDomainContext;
_dsFeedback.QueryName = "GetOrderedFeedbacks";
_dsFeedback.PageSize = 10;
_dsFeedback.Load();
and provide a bindable property :
private DomainDataSource _dsFeedback { get; set; }
public DomainDataSource Feedback
{
get
{
return _dsFeedback;
}
}
And add your DataPager to your XAML:
<data:DataPager Grid.Row="1"
HorizontalAlignment="Stretch"
Source="{Binding Feedback.Data}"
Margin="0,0,0,5" />
<data:DataGrid ItemsSource="{Binding Feedback.Data}">
PS. Thanks to 'Francois' from the above linked page. I didn't even realize I could take the DomainDataSource out of the XAML until I saw your comment!
This is an interesting article from May 2010 about the possible future support for this type of feature in the framework.
http://www.riaservicesblog.net/Blog/post/WCF-RIA-Services-Speculation-EntityCollectionView.aspx

Resources