SugarCRM 7 SugarQuery() join statement with custom modules - mysql

I'm trying to write a simple query in Sugar 7.2 that will join a table. This SQL is what I'm trying to create in the Sugar framework using SugarQuery().
SELECT * FROM keyname_table_a LEFT JOIN keyname_table_b
So what I've created is the following. It works just fine when I use the core modules, but if I switch it to custom modules that we've created, Sugar logs me out and brings me to a login prompt.
$query = new SugarQuery();
$query->from(BeanFactory::getBean('keyname_table_a'));
$query->join('keyname_table_b');
$results = $query->execute();
return print_r($results, true);
The above logs me out and gives me the following error message in a popup message (similar to how app.alert.show works), and logs me out.
Error: Request failure, or, missing/invalid parameter. Please contact technical support
If I replace the "from" and "join" tables to be "Accounts" and "cases" respectively, then the query works just fine and pulls in the expected result. However, switching it to the custom modules that we have results in the above error and we are logged out instantly.
I have verified that the relationship exists and there is data linking the two together. It is a one to many relationship in Sugar. table_a is one, while table_b is many.
What is the proper way to join two custom modules with the SugarQuery() object?

You need to pass link field name instead of table_name or relationship name
$query = new SugarQuery();
$query->from(BeanFactory::getBean('keyname_table_a'));
$query->join('link_name');
$results = $query->execute();

The join should be the relationship name. If you would like to join a table directly you can use joinTable().
In the Accounts example above, cases is the relationship name, not the table name.
Have a great day!

Related

Doctrine Query Builder with joins resulting in: "Unknown column id in 'on clause'"

Summary
I'm trying to use Doctrine's Query Builder to make a SELECT that includes a number of joins.
This works fine when all the joins are done through entity relationships, but when I introduce a join not through a relationship (campaign_instance below), it results in a bad MySQL query.
The error is "Unknown column 'w3_.id' in 'on clause'". w3_ refers to a table that absolutely has an id column.
The code
This query builder:
$this->entity_manager->getRepository(CampaignStep::class)
->createQueryBuilder('campgain_steps_available_for_completion')
->select('step')
->from(CampaignStep::class, 'step')
->join('step.group', 'campaign_group')
->join('campaign_group.campaign', 'campaign')
->join(
CampaignInstance::class,
'campaign_instance',
'WITH',
'campaign_instance.campaign = campaign')
->getQuery()
->execute();
results in this MySQL query:
SELECT
t0_.id AS id_0,
t0_.slug AS slug_1,
t0_.description AS description_2,
t0_.active AS active_3,
t0_.created_at AS created_at_4,
t0_.step_completion_min AS step_completion_min_5,
t0_.ordinal AS ordinal_6,
t0_.dtype AS dtype_7,
t0_.mobile_image_id AS mobile_image_id_8,
t0_.web_image_id AS web_image_id_9,
t0_.group_id AS group_id_10
FROM
taxonomy_topic t1_
INNER JOIN campaign_instance w2_ ON (w2_.campaign_id = w3_.id),
taxonomy_topic t0_
INNER JOIN campaign_group w4_ ON t0_.group_id = w4_.id
INNER JOIN campaign w3_ ON w4_.campaign_id = w3_.id
AND w3_.dtype IN ('campaign')
WHERE
(
t1_.dtype IN ('campaignstep')
AND t0_.dtype IN ('campaignstep')
)
...Which results in this error:
Unknown column 'w3_.id' in 'on clause'
Entity summary
Here are the entity relationships:
Campaign <-1-M-> CampaignGroup <-1-M-> CampaignStep
Campaign <-1-M- CampaignInstance
I'm selecting CampaignSteps, joining them to CampaignGroup then Campaign through their entity relationships. That much works.
Then I want to join from Campaign to CampaignInstance. Since that relationship is unidirectional (owning side CampaignInstance), I can't use a Campaign relationship to get there. Adding this:
->join(
CampaignInstance::class,
'campaign_instance',
'WITH',
'campaign_instance.campaign = campaign')
to the Query Builder breaks the resulting query.
How do I fix this?
Packages
composer show doctrine/*:
doctrine/annotations 1.13.2 Docblock Annotations Parser
doctrine/cache 1.12.1 PHP Doctrine Cache library is a popular cache implementation that supports ...
doctrine/collections 1.6.8 PHP Doctrine Collections library that adds additional functionality on top ...
doctrine/common 2.13.3 PHP Doctrine Common project is a library that provides additional functiona...
doctrine/dbal 2.13.7 Powerful PHP database abstraction layer (DBAL) with many features for datab...
doctrine/deprecations v0.5.3 A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging w...
doctrine/doctrine-bundle 2.3.2 Symfony DoctrineBundle
doctrine/doctrine-migrations-bundle 2.2.3 Symfony DoctrineMigrationsBundle
doctrine/event-manager 1.1.1 The Doctrine Event Manager is a simple PHP event system that was built to b...
doctrine/inflector 1.4.4 PHP Doctrine Inflector is a small library that can perform string manipulat...
doctrine/instantiator 1.4.0 A small, lightweight utility to instantiate objects in PHP without invoking...
doctrine/lexer 1.2.3 PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive D...
doctrine/migrations 2.3.5 PHP Doctrine Migrations project offer additional functionality on top of th...
doctrine/orm 2.7.5 Object-Relational-Mapper for PHP
doctrine/persistence 1.3.8 The Doctrine Persistence project is a set of shared interfaces and function...
doctrine/reflection 1.2.2 The Doctrine Reflection project is a simple library used by the various Doc...
doctrine/sql-formatter 1.1.2 a PHP SQL highlighting library
What I've tried so far:
This seems to have something to do with the multiple FROM clauses.
If I remove the first clause taxonomy_topic t1_ INNER JOIN workspace_instance w2_ from the FROM (and its related WHERE clause), I get a valid query that gives me exactly what I'm looking for:
SELECT
t0_.id AS id_0,
t0_.slug AS slug_1,
t0_.description AS description_2,
t0_.active AS active_3,
t0_.created_at AS created_at_4,
t0_.step_completion_min AS step_completion_min_5,
t0_.ordinal AS ordinal_6,
t0_.dtype AS dtype_7,
t0_.mobile_image_id AS mobile_image_id_8,
t0_.web_image_id AS web_image_id_9,
t0_.group_id AS group_id_10
FROM
taxonomy_topic t0_
INNER JOIN workspace_group w4_ ON t0_.group_id = w4_.id
INNER JOIN workspace w3_ ON w4_.campaign_id = w3_.id
AND w3_.dtype IN ('campaign')
WHERE
(
t0_.dtype IN ('campaignstep')
)
However, I don't know how to alter the Doctrine Query Builder to result in that MySQL.
Figured it out.
Since I'm creating the Query Builder from the CampaignStep repository, the CampaignStep select was implicit. By adding ->select('step')->from(CampaignStep::class, 'step'), it was trying to join CampaignStep again, resulting in the multiple FROM clauses that broke the query.
This is the fixed Query Builder:
$this->mymeq_em->getRepository(CampaignStep::class)
->createQueryBuilder('step')
->join('step.group', 'campaign_group')
->join('campaign_group.campaign', 'workspace')
->join(
WorkspaceInstance::class,
'workspace_instance',
'WITH',
'workspace_instance.workspace = workspace')
->getQuery()
->execute();
Read https://dev.mysql.com/doc/refman/8.0/en/join.html in the sections where it says:
An ON clause can refer only to its operands.
JOIN has higher precedence than the comma operator (,)
I don't use Doctrine, so I can't advise on how to make its query builder do what you want. But the semantics of JOIN documented at that manual page explain why you got the errors you saw.

Replacing existing View but MySQL says "Table doesn't exist"

I have a table in my MySQL database, compatibility_core_rules, which essentially stores pairs of ids which represent compatibility between parts which have fields with those corresponding ids. Now, my aim is to get all possible compatibility pairs by following the transitivity of the pairs (e.g. so if the table has (1,2) and (2,4), then add the pair (1,4)). So, mathematically speaking, I'm trying to find the transitive closure of the compatibility_core_rules table.
E.g. if compatibility_core_rules contains (1,2), (2,4) and (4,9), then initially we can see that (1,2) and (2,4) gives a new pair (1,4). I then iterate over the updated pairs and find that (4,9) with the newly added (1,4) gives me (1,9). At this point, iterating again would add no more pairs.
So my approach is to create a view with the initial pairs from compatibility_core_rules, like so:
CREATE VIEW compatibility_core_rules_closure
AS
SELECT part_type_field_values_id_a,
part_type_field_values_id_b,
custom_builder_id
FROM compatibility_core_rules;
Then, in order to iteratively discover all pairs, I need to keep replacing that view with an updated version of itself that has additional pairs each time. However, I found MySQL doesn't like me referencing the view in its own definition, so I make a temporary view (with or replace, since this will be inside a loop):
CREATE OR REPLACE VIEW compatibility_core_rules_closure_temp
AS
SELECT part_type_field_values_id_a,
part_type_field_values_id_b,
custom_builder_id
FROM compatibility_core_rules_closure;
No problems here. I then reference this temporary view in the following CREATE OR REPLACE VIEW statement to update the compatibility_core_rules_closure view with one iteration's worth of additional pairs:
CREATE OR REPLACE VIEW compatibility_core_rules_closure
AS
SELECT
CASE WHEN ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_a THEN ccr1.part_type_field_values_id_b
WHEN ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_b THEN ccr1.part_type_field_values_id_b
END ccrA,
CASE WHEN ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_a THEN ccr2.part_type_field_values_id_b
WHEN ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_b THEN ccr2.part_type_field_values_id_a
END ccrB,
ccr1.custom_builder_id custom_builder_id
FROM compatibility_core_rules_closure_temp ccr1
INNER JOIN compatibility_core_rules_closure_temp ccr2
ON (
ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_a OR
ccr1.part_type_field_values_id_a = ccr2.part_type_field_values_id_b
)
GROUP BY ccrA,
ccrB
HAVING -- ccrA and ccrB are in fact not the same
ccrA != ccrB
-- ccrA and ccrB do not belong to the same part type
AND (
SELECT ptf.part_type_id
FROM part_type_field_values ptfv
INNER JOIN part_type_fields ptf
ON ptfv.part_type_field_id = ptf.id
WHERE ptfv.id = ccrA
LIMIT 1
) !=
(
SELECT ptf.part_type_id
FROM part_type_field_values ptfv
INNER JOIN part_type_fields ptf
ON ptfv.part_type_field_id = ptf.id
WHERE ptfv.id = ccrB
LIMIT 1
)
Now this is where things go wrong. I get the following error:
#1146 - Table 'db509574872.compatibility_core_rules_closure' doesn't exist
I'm very confused by this error message. I literally just created the view/table only two statements ago. I'm sure the SELECT query itself is correct since if I try it by itself and it runs fine. If I change the first line to use compatibility_core_rules_closure2 instead of compatibility_core_rules_closure then it runs fine (however, that's not much use since I need to be re-updating the same view again and again). I've looked into the SQL SECURITY clauses but have not had any success. Also been researching online but not getting anywhere.
Does anyone have any ideas what is happening and how to solve it?
MySQL doesn't support sub-queries in views.
You'll have to separate them... ie. using another view containing the sub-query inside you main view.
Running the create statement for that view will render an error, not creating it, hence the doesn't exist error you're getting when querying it.

MultipleObjectsReturned error in django admin - but there is no duplication in database

I'm having a problem in the Django admin. I'm using version 1.5.5
I have a Booth model which has a foreign key to my AreaLayout model, which then goes back through another few models with foreign and many2many keys. My model code can be seen on pastebin. The initial indication of the problem in admin is that AreaLayouts are being duplicated in the select dropdown in Booth admin. The MultipleObjectsReturned error (traceback) is being raised when I then try to save a new booth. I was able to trace this back to the SQL query that django is creating to grab the AreaLayout list:
SELECT `venue_arealayout`.`id`, `venue_arealayout`.`name`, `venue_arealayout`.`description`, `venue_arealayout`.`area_id`, `venue_arealayout`.`additional_notes`
FROM `venue_arealayout`
INNER JOIN `venue_area` ON (`venue_arealayout`.`area_id` = `venue_area`.`id`)
INNER JOIN `venue_venue` ON (`venue_area`.`venue_id` = `venue_venue`.`id`)
INNER JOIN `venue_venue_venue_type` ON (`venue_venue`.`id` = `venue_venue_venue_type`.`venue_id`)
INNER JOIN `venue_venuetype` ON (`venue_venue_venue_type`.`venuetype_id` = `venue_venuetype`.`id`)
WHERE (`venue_arealayout`.`id` = 66 )
This query produces a duplicate in MySQL when I run it there. Removing the final 2 JOINs results in a single result being returned (which is the desired result), whereas removing only the last join still results in duplication.
I tried running that query with SELECT * in place of selecting specific fields and the two results in that case are almost equal. The difference is that the venue in question has multiple venuetypes and I'm getting a result for each of those. Is there any way I can tell django not to include those joins for these queries, or is there a way I can get distinct results as far as AreaLayouts go?
I think you're being caught by the bug reported in ticket 11707. One of the comments mentions that it can cause a MultipleObjectsReturned exception.
All I can suggest is that you stop using limit_choices_to. Your models are fairly complex, so I can't immediately see which one is causing the problem.

Doctrine 2 HAVING on a alias fails in a subquery

I have a query which works fine on its own. But when using it as a subquery it somehow fails to recognize the alias, instead, the parser thinks it belongs to a class.
To explain what im trying to achieve:
Lets say for example we have a Hotel with Rooms(each room is a different type) and we only want the Hotels that contains specific room types.
A WHERE in a JOIN would work, but that would only JOIN the Rooms that match the criteria. I want Hotels that match the criteria but still select all the rooms.
This is your every day SQL solution so i figured im probably doing something wrong..
Example root query:
$main = $em->createQueryBuilder()
->select('hotels, rooms')
->from('Hotel', 'hotels')
->leftJoin('hotels.Rooms', 'rooms');
Subquery:
$sub = $em->createQueryBuilder()
->select('count(rooms.id) as hidden totalRooms')
->from('HotelToRoom', 'rooms')
->andWhere('rooms.hotel_id = hotels.id') // or a dummy ID as a solo query..
->andWhere($qb->expr()->in('rooms.type_id', implode(',', $types)))
->having('totalRooms = ?1')
->setParameter('1', count($types))
;
The subquery works fine solo. But when I add it as a subquery like:
$main->andWhere($qb->expr()->exists($sub->getDQL()));
The following error erupts:
Error: Class 'totalRooms' is not defined.'
It seems that the having part no longer searches inside the subquery or something...?

MySQL Select* returns everything but the id column

I have a database with 2 tables I wish to pull data from both and display it in an html table. When I execute the below select statement I seem to get everything but the auto_increment column "id"
>mysql connection strings here.
$query = "SELECT Hotels.*,surfcup_Rates.*
FROM Hotels
LEFT JOIN surfcup_Rates
ON Hotels.id=surfcup_Rates.hotelID";
Then I use load the query into $result.
Later I load it into mysql_fetch_array and loop it into an html table.
while ($row = mysql_fetch_array($result)) {
echo "</td><td>";
echo 'Delete';
echo "</td><td>";
echo $row['id'];
echo "</td><td>";
echo $row['preferred'];
>and so on...........
Everything works: the join, the select statement and data loads into the table
perfectly. All except the id column from the first table. I wish to use the id to allow the user to delete a record. The user will be adding data to the tables from a password protected "back-end" for data entry.
Is there something wrong with my select statement? Or am I missing something. Any help will be greatly appreciated.
Thank You,
Dave.
Assuming that your surfcup_Rates table has an id column (you don't say that explicitly, but it seems likely), then you either need to "rename" one of the id columns from the two tables so they don't collide when mysql_fetch_array figures out what to return for each row, or I believe you can just swap the two parts of the SELECT output selectors, given that you don't seem to care about the id from the surfcup_Rates table:
$query = "SELECT surfcup_Rates.*,Hotels.*
FROM Hotels
LEFT JOIN surfcup_Rates
ON Hotels.id=surfcup_Rates.hotelID";
In this case, the id from Hotels will mask the one from surfcup_Rates, not the other way around as you have it.
Try
$query = "SELECT *
FROM Hotels
LEFT JOIN surfcup_Rates
ON Hotels.id=surfcup_Rates.hotelID";
NOTE: I would probably do all the fields in the select
SELECT Hotels.id as HotelId, surfcup_Rates.id as SurfCupId,....
[others here] ... FROM Hotels
LEFT JOIN surfcup_Rates
ON Hotels.id=surfcup_Rates.hotelID
Does the surfcup_Rates table also have a field called id? If so, mySQL won't know whatid to use, hotel or surfcup_Rates.
In this case you have a couple of options,
changing the table structure, but do so at your own risk as other things could break
use an alias (best method)
DISCLAIMER
I have never used the following method in PHP myself, I have seen it in SAP and Zimbra, as this isn't a discussion forum on these aspects, instead of a debate I thought I would put a disclaimer that it may not work but as mentioned above and in my comment below I have seen it work
call hotels.id
I hope this helps