I'm trying to join multiple datasources in a grid I created.
The grid is in CaseDetail form and it uses same tables as some other groups on it.
Therefore I have to use joins based on datasources names and not on tables names.
There is InventTable(InventTableComplaints) - parent and EcoResProductTranslation(EcoResProductTranslationComplaints) - child.
What I'm trying to do is to add this code to the child datasource:
public void executeQuery()
{
QueryBuildDataSource qbdsIT, qbdsERPTC;
qbdsIT = InventTableComplaint_DS.queryBuildDataSource();
qbdsERPTC = qbdsIT.addDataSource(tableNum(EcoResProductTranslation), "EcoResProductTranslationComplaint");
qbdsERPTC.addLink(fieldNum(InventTable, Product), fieldNum(EcoResProductTranslation, Product));
qbdsERPTC.joinMode( JoinMode::OuterJoin );
qbdsERPTC.fetchMode( QueryFetchMode::One2One );
super();
}
But it doesn't work.
Is it possible?
You do not define the table relations in the executeQuery method, do so in the init method instead which is executed exactly once. If you defined the datasource in the form (using InventTableComplaint as JoinSource and with OuterJoin as JoinMode), you do not need to do it in init method either, but you may need to define the link if not provided as table relations:
public void init()
{
qbdsIT = InventTableComplaint_DS.queryBuildDataSource();
qbdsERPTC = EcoResProductTranslationComplaint_ds.queryBuildDataSource();
qbdsERPTC.clearLinks();
qbdsERPTC.addLink(fieldNum(InventTable, Product), fieldNum(EcoResProductTranslation, Product));
super();
}
Beware that more than one record of may exist of EcoResProductTranslation for each InventTable (on for each language), so you may end out with "duplicates" of InventTable in the grid.
Related
I'm in a Laravel 9 project trying to access extra relationship data in a pivot table - I'm sure this will have been answered before but the answers are confusing me more!
I have a set of courses, a set of cohorts and a set of sessions. Each course can be run multiple times (each one of these I've called an instance), with multiple sessions, and one cohort.
I've created an instances table in my database which has:
course_id
cohort_id
I have a sessions table which has data on each individual session, for example a name, a review date and review status.
I then have a pivot table called instance_sessions which looks like this:
instance_id
session_id
date
trainer_id
zoom_room_id
cohort_id
I'm not sure I need the cohort id but thats by the by :-)
My question is how can I get the relationships from this pivot table to the trainer and the zoom room?
In my instance model my relationships are setup like this:
public function sessions(){
return $this->belongsToMany(Session::class)->withPivot(['date'])
->using(InstanceSession::class);
}
public function course(){
return $this->belongsTo(Course::class);
}
public function cohort(){
return $this->hasOne(Cohort::class, 'id', 'cohort_id');
}
Session model:
function instances(){
return $this->belongsToMany(Instance::class);
}
Instance Session Model:
class InstanceSession extends Pivot
{
use HasFactory;
protected $table = 'instance_session';
protected $fillable = ['instance_id', 'session_id', 'date', 'trainer_id', 'zoom_room_id', 'cohort_id' ];
public function instance(){
return $this->belongsTo(Instance::class);
}
public function session(){
return $this->belongsToMany(Session::class);
}
public function zoomRoom(){
return $this->belongsTo(ZoomRoom::class);
}
public function trainer(){
return $this->hasOne(Trainer::class);
}
}
And my Instance Controller:
$instances = Instance::with(['course', 'cohort', 'sessions'])->get();
which returns the data to an Inertia/Vue view. I've attached an image of the data being retrieved.
Any help would be gratefully received as I'm clearly not understanding something :-)
EDIT: End goal being that I want to show on the front end all of the instances with all the sessions that belong to that instance, the date those sessions take place on, the trainer name (from trainers table) taking the session and the zoom room link (from zoom room table) for the session.
In my Form, I have Two TabPages, with the same DataSource for each.
In TabPageA I have all records from my DataSource. I select records in TabPageA and I want to only show, in TabPageB, those records I selected previously in TabPageA.
If I don't select anything in TabPageA I don't see anything in TabPageB
For example if in my GridA I selected Record#4 , in GridB I see only Record#4.
I don't think you'll be able to filter TabPageB differently from TabPageA easily due to the fact they share the same DataSource.
There are two quick ways to modify this.
Keep it as one DataSource:
Create a global QueryBuildRange to easily update the range on selected records.
public class FormRun extends ObjectRun
{
QueryBuildRange qbr;
}
Override the init of your datasource. Here you will set the qbr:
public void init()
{
super();
qbr = this.queryBuildDataSource().addRange(fieldNum(SalesTable, RecId));
}
Then override the pageActivated TabPage method for each to set the range and re-execute the query to your liking:
TabPageA:
public void pageActivated()
{
super();
qbr.value('');
SalesTable_ds.executeQuery();
}
TabPageB
public void pageActivated()
{
super();
element.createRange();
SalesTable_ds.executeQuery();
}
Method to update range:
public void createRange()
{
MultiSelectionHelper multiSelectionHelper = MultiSelectionHelper::createFromCaller(this);
common common;
Set set = new Set(Types::Int64);
// Necessary if the datasource you want is not the header datasource.
multiSelectionHelper.parmDatasource(SalesTable_ds);
common = multiSelectionHelper.getFirst();
while (common)
{
// Use of set not necessary, but a little cleaner.
set.add(common.RecId);
common = MultiSelectionHelper.getNext();
}
qbr.value(strRem(set.toString(), '"{}'));
}
This is somewhat dirty, but you get the idea and could clean it up as needed.
Two Datasources:
Update the range on the second datasource (used by TabPageB) based on the multi-selection of the first datasource. But I'm assuming you only want one datasource for some reason.
I am creating an application in zend framework. Now i am stuck in the Zend table relationships.
I have 2 tables. I have set the primary key and the corresponding references in other tables.
Now i want to join the two tables using the relationships (not with the join functions). Is it possible in zend?
the tables structures are like the one below
Schemetable
scheme_id primary key
Scheme_name
Scheme_Desc
Ratestable
rate_id
Scheme_id *foreign key ref scheme_id*
rates:
Time periods:
There is an one to many relation b/w the scheme and rates
I have done some coding in the model classes
Scheme.php
class Scheme extends Zend_Db_Table_Abstract {
protected $_name = 'schemetable';
protected $_dependentTables = array('rates');
}
Rates.php
class Rates extends Zend_Db_Table_Abstract {
protected $_name = 'ratetable';
protected $_referenceMap = array(
'Scheme' => array(
'columns' => array('scheme_id'),
'refColumns' => array('scheme_id'),
'refTableClass' => 'Scheme',
),
);
}
How can i fetch every scheme and their corresponding rates?
Thanks in advance.
Please,
see the DOCS:
http://framework.zend.com/manual/en/zend.db.table.relationships.html
Fetching a Dependent Rowset
If you have a Row object as the result
of a query on a parent table, you can
fetch rows from dependent tables that
reference the current row. Use the
method:
$row->findDependentRowset($table);
Example #4 Fetching Dependent Rowsets
using the Magic Method
This example shows finding dependent
Rowsets equivalent to those in the
previous examples. In this case, the
application uses the magic method
invocation instead of specifying the
table and rule as strings.
$accountsTable = new Accounts();
$accountsRowset = $accountsTable->find(1234);
$user1234 = $accountsRowset->current();
// Use the default reference rule
$bugsReportedBy = $user1234->findBugs();
// Specify the reference rule
$bugsAssignedTo = $user1234->findBugsByEngineer();
Fetching a parent row
If you have a Row object as the result
of a query on a dependent table, you
can fetch the row in the parent to
which the dependent row refers. Use
the method:
$row->findParentRow($table);
This example shows finding parent Rows
equivalent to those in the previous
examples. In this case, the
application uses the magic method
invocation instead of specifying the
table and rule as strings.
$bugsTable = new Bugs();
$bugsRowset = $bugsTable->fetchAll(array('bug_status = ?', 'NEW'));
$bug1 = $bugsRowset->current();
// Use the default reference rule
$reporter = $bug1->findParentAccounts();
// Specify the reference rule
$engineer = $bug1->findParentAccountsByEngineer();
I have an object that has been populated with the contents of four different related entities. However i have another entity in which i cannot include as part of the query due to it not being related in the navigation properites directly to the IQueryable table i am pulling. The entity i am trying to include is related to one of the four different entities that have been included successfully.
Is there a way to include(during db hit or afterwards) this entity as part of the overall object i am creating?
Here is an example of what my calls look like to build the CARTITEM object:
public List<CARTITEM> ListCartItem(Guid cartId)
{
//Create the Entity object
List<CARTITEM> itemInfo = null;
using (Entities webStoreContext = new Entities())
{
//Invoke the query
itemInfo = WebStoreDelegates.selectCartItems.Invoke(webStoreContext).ByCartID(cartId).ToList();
}
//Return the result set
return itemInfo;
}
here is the selectCartItems filter(Where i would normally do the includes):
public static Func<Entities, IQueryable<CARTITEM>> selectCartItems =
CompiledQuery.Compile<Entities, IQueryable<CARTITEM>>(
(cart) => from c in cart.CARTITEM.Include("ITEM").Include("SHIPPINGOPTION").Include("RELATEDITEM").Include("PROMOTION")
select c);
from this i have my CARTITEM object. Problem is i want to include the PROMOTIONTYPE table in this object, but since the CARTIEM entity doesn't have a navigation property directly to the PROMOTIONTYPE table i get an error.
Let me know if you need any more clarification.
Thanks,
Billy
You can use join and if it is the same database and server it should generate the join in SQL and do it all in one call...
LinqToEnties join example
I have an entity with self reference (generated by Entity Designer):
public MyEntity: EntityObject
{
// only relevant stuff here
public int Id { get...; set...; }
public MyEntity Parent { get...; set...; }
public EntityCollection<MyEntity> Children { get...; set...; }
...
}
I've written a stored procedure that returns a subtree of nodes (not just immediate children) from the table and returns a list of MyEntity objects. I'm using a stored proc to avoid lazy loading of an arbitrary deep tree. This way I get relevant subtree nodes back from the DB in a single call.
List<MyEntity> nodes = context.GetSubtree(rootId).ToList();
All fine. But when I check nodes[0].Children, its Count equals to 0. But if I debug and check context.MyEntities.Results view, Children enumerations get populated. Checking my result reveals children under my node[0].
How can I programaticaly force my entity context to do in-memory magic and put correct references on Parent and Children properties?
UPDATE 1
I've tried calling
context.Refresh(ClientWins, nodes);
after my GetSubtree() call which does set relations properly, but fetches same nodes again from the DB. It's still just a workaround. But better than getting the whole set with context.MyEntities().ToList().
UPDATE 2
I've reliably solved this by using EF Extensions project. Check my answer below.
You need to assign one end of the relationship. First, divide the collection:
var root = nodes.Where(n => n.Id == rootId).First();
var children = nodes.Where(n => n.Id != rootId);
Now, fix up the relationship.
In your case, you'd do either:
foreach (var c in children)
{
c.Parent = root;
}
...or:
foreach (var c in children)
{
root.Children.Add(c);
}
It doesn't matter which.
Note that this marks the entities as modfied. You'll need to change that if you intend to call SaveChanges on the context and don't want this saved.
The REAL solution
Based on this article (read text under The problem), navigation properties are obviously not populated/updated when one uses stored procedures to return data.
But there's a nice manual solution to this. Use EF Extensions project and write your own entity Materilizer<EntityType> where you can correctly set navigation properties like this:
...
ParentReference = {
EntityKey = new EntityKey(
"EntityContextName.ParentEntitySetname",
new[] {
new EntityKeyMember(
"ParentEntityIdPropertyName",
reader.Field<int>("FKNameFromSP")
)
})
}
...
And that's it. Calling stored procedure will return correct data, and entity object instances will be correctly related to eachother. I advise you check EF Extensions' samples, where you will find lots of nice things.