How select a single row october cms - mysql

How to select a single row on october cms?
How can a simple thing be so complicated here?
I thought it would be something to help us and not to disturb something that is as simple as
SELECT * FROM `engegraph_forms_membros`
Here it's like fighting against demons without a bible, oh god why?
Why make the query difficult for newbie?

I understand you don't speak English natively but you should watch every single one of these videos.
Does the record belong to a model in a plugin? Here are the docs on how to work with models.
You make a plugin, set the database which creates models, and then make components to be ran in your CMS Pages.
In a component.php file you can have something like this: Here I am calling the model class Agreements with use Author\Plugin\Models\Agreements;. This allows me to run a function/method to retrieve all agreements or one agreements using laravel's eloquent collection services.
Lets say we have the ID of a record. Well we can either call on the Agreements model with ::find or with ::where. You will noticed I have two functions that essentially do the same thing. ::find uses the primary key of the models (in my case the id) and will return a singular record. *Note that find can take an array and return a collection of records; like ::where. Using ::where we are going to look for the ID. *Note ::where always returns a collection which is why I have included ->first().
<?php namespace Author\Plugin\Components;
use Session;
use Input;
use Crypt;
use Db;
use Redirect;
use Illuminate\Contracts\Encryption\DecryptException;
use October\Rain\Support\Collection;
use Author\Plugin\Models\Agreements;
class GetAgreement extends \Cms\Classes\ComponentBase
{
public function componentDetails()
{
return [
'name' => 'Get one agreement',
'description' => 'Get an agreement to change or delete it'
];
}
public function onRun() {
$this->page['agreement'] = $this->getWithFindAgreement;
}
public function getWithFindAgreement() {
$id = 1;
$agreement = Agreements::find($id);
return $agreement;
}
public function getWithWhereAgreement() {
$id = 1;
$agreement = Agreements::where($id)->first();
return $agreement;
}
}
If for some reason you aren't working with models, here are the docs to work with Databases. You will have to register the use Db; facade.
Here call the table you want and use ::where to query it. *Note the use of ->first() again.
$users = Db::table('users')->get();
$user = $users->where('id', 1)->first();

There are two simple ways to select a single row:
This will give you the'first' record in the selected recordset.
SELECT top 1 * FROM `engegraph_forms_membros`
This will select all the records that meet the predicate requirement that the value of <columnname> is equal to <value>
SELECT * FROM `engegraph_forms_membros` where <columnname>=<value>
If you select a record where multiple values meet that requirement, then you can (randomly) pick one by combining the solutions...
SELECT top 1 * FROM `engegraph_forms_membros` where <columnname>=<value>
But be aware that without an ORDER BY clause, the underlying data is unordered and prone to change uncontrollably, which is why most people (including your boss) will find the use of 'Top' to be improper for real use.

Related

Yii2 - Assign a "fix" condition

I am building an app, where an account can have many services, all the information is related to a service. In example:
Account A has 3 services and each service has pages.
In order to avoid someone modifying the service_id when saving a page, at the moment I do:
if(Yii::$app->request->isPost) {
$post = Yii::$app->request->post();
$model->load($post);
$model->service_id = $this->service->id;
}
Where $model->service_id = $this->service->id helps me assign the selected service_id after loading table to model and avoid someone sending service_id from the form.
But in case someone in the future, develops "documents" I would like to avoid the developer handling this service_id on all the queries.
So First it I thought I could try:
public function beforeFind($queryData) {
parent::beforeFind();
$queryData['conditions'] = array('service_id' => 2);
return $queryData;
}
But still needs the developer to implement it. So maybe is there a way to create a "BaseService" model where all other service related models should extend from but not sure how to:
Add the condition from the parent model?
How to pass the id to this model so it keeps it during all queries?
Maybe there is a simple solution, and I am overcomplicating myself due long hours working, not sure.
That is a default condition to apply for all queries. In case your application is built on top of ActiveRecord classes (not performing direct SQL queries or on the fly QueryBuilder) then you can simply override the find() method inside your Model class:
public static function find()
{
/* you can add more dynamic logic here */
return parent::find()->where(['service_id' => 2]);
}
By default, all controllers in Yii2 are using Model::find() to retrieve data from database, adding such condition should be enough to not retrieve anything with a different service_id than 2. Direct http GET requests by ID should then output 404 if that condition isn't satisfied and retrieving them as relational data within a different model class should return a filtered array.
IMPORTANT: To not break that implementation you need to:
Always use
ActiveRecord. Otherwise you'll need to manually add the condition to your queries.
(This is not correct) Be carful on when to use asArray() as it omits ActiveRecord features (See note and explanation
here). Otherwise you need to manually re-declare the condition like: Account::find()->where(['service_id' => 2])->asArray()->all();
Always use andWhere() to merge conditions because where() will override/ignore the default one. Example: Account::find()->andWhere('age>30')->all();
to reuse such filters you can put them into a custom ActiveQuery.
in your ActiveRecord:
public static function find() {
return (new ActiveQuery(get_called_class()));
}
ActiveQuery:
public function service($service = 2) {
return $this->andWhere(['service_id' => $service]);
}
your model based on your ActiveRecord:
public static function find() {
return (new ActiveQuery(get_called_class()))->service(2);
}
alternatively
$model->find()->service(1);
also, this might be of interest (setting Default values per scenario)

Yii2: ActiveQuery "with" not working

Circumstances
I have three models/db-tables related with 1:n each: An order has multiple commissions and a commission has multiple commission_positions. Therefore the commission_position has an FK-field containing a commission id. The commission itself has an FK-field containing the id of an order.
Order > Commission > CommissionPositions
Problem
What I need to do now is select all the CommissionPositions having a certain value in the related Order-Model. Obvious solution is to use the Query-Object of CommissionPosition which I extended with a named scope. The named scope looks like this:
class CommissionPositionQuery extends ActiveQuery
{
/**
* Named scope to filter positions of a certain alpha order id
* #param integer $id the alpha order id
* #return \common\models\query\CommissionPositionQuery
*/
public function alphaOrderId($id)
{
//TODO: with not working
$this->with(['commission.order']);
$this->andWhere(['alpha_order_id'=>$id]);
return $this;
}
}
The relation commission is defined on the Commission-Model and working. The second relation order is defined on the commission-model and working as well. The filtered field alpha_order_id is in the Order-Table. Now I execute the query like this:
$filteredPositions = CommissionPosition::find()->alphaOrderId(17)->all();
The scope is called successfully and the where-part is used, but when I check the generated SQL I see no join-statements even though I use the with-method to tell yii to fetch the relation together. The response is 'unknown column alpha_order_id' which makes sense as there is no join to the related tables. This is the generated SQL:
SELECT * FROM `commission_position` WHERE (`alpha_order_id`=17)
What am I missing? Is this a bug of Yii2?
Found the soution myself. The naming changes between Yii1 and Yii2 lead to a little confusion. To prevent others from wasting time here the details:
Yii1
In yii 1 you would join in a relation (exemplary: commission) directly like this:
$query->with = 'commission'
$query->together = true;
Yii2 / difference
When calling the with-method like showed in the question the relation was successfully added to the with-array of the ActiveQuery. However, when executing the query, the join part was missing.
Solution
Seems like the with-method is NOT the way to go. Instead I used the method called joinWith with the following signature:
public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')
Now as described in the answer I defined the relation as the first argument ('commission.order') and left the rest as is, because the default values are perfectly fine. Pay attention to the default value of the second parameter. this makes sure the relations are joined in directly!
VoilĂ ...the resulting sql contains the needed joins. One pitfall is to be considered though: Ambigious column namings is of course to be handled by ourselves! Link to the documentation of the method:
http://www.yiiframework.com/doc-2.0/yii-db-activequery.html#joinWith()-detail
If you want a JOIN use:
$this->joinWith(['commission.order']);

CakePHP virtual field: replace one string with another

I wonder if there is any way to declare a virtual field in CakePHP to do the following:
We have to replace a user's status with a symbol and append to it the user's nickname. For example, if a user is an admin, we want to display: #barth, for a regular user ~barth.
I already wrote an afterFind() callback to perform this task, but it fails using the containable behavior.
Either is there another way to implement it, or we can create a virtual field. The latter solution would be very elegant, but after googling I cannot find any way to use MySQL syntax to replace one string with another.
Ideas?
Virtual fields are very easy to use in Cake. You can use any regular MySQL function in their declaration to achieve this type of thing.
You'll first need to determine the SQL command to achieve what you want, I'd suggest using the CONCAT() function:
-- Return an # concatenated onto the username
CONCAT('#', yourfield)
Then add this as a virtual field in your model:
class YourModel extends AppModel {
public $virtualFields = array(
'yourVirtualField' => 'CONCAT("#", yourfield)'
);
}
Now, when you query this model you should be able to access it like this:
$example = $this->YourModel->find('first');
echo $example['YourModel']['yourVirtualField']; // #yourfield
Edit
Since your update, you've got the values you want to concatenate together in another model as virtual fields already. CakePHP doesn't allow you to use associated models' virtual fields when creating a new virtual field, but you can do a subselect query to manually get this data. Here's an SQL Fiddle example.

Trying to restrict an Eloquent query to a relationship with a count of 0

I have two models (Organizations and Interactions) and I'd like to query the Organization model for all of the Orgs that have no Interactions. Organizations have a one-to-many relationship with Interactions.
I tried looking into anti-joins in raw SQL, but got nowhere. I also wanted to totally avoid anything like getting all of the full Organizations, then iterating through them to check to see if they had any Interactions, because that's completely impractical given the amount of data I'm working with.
To clarify, I want to avoid this:
$organizations = Organization::all();
foreach ($organizations as $org)
if($org->interactions()->count() == 0){
//Add the org to an array for later use because it has no interactions
}
I'm using Laravel 3.x, and I can't upgrade because the project is really big and I don't have the month it would take to upgrade to 4.1 right now. If there's a significantly better way to do stuff like this 4, that would make selling the conversion process easier.
Here's some relevant code:
//From organization.php
public function interactions() {
return $this->has_many('Interaction');
}
//From interaction.php
public function organization() {
return $this->belongs_to('Organization');
}
// select all Organization IDs that have at least 1 interaction
$uniqueOrganizationIDs = DB::raw('SELECT organization_id FROM interactions GROUP BY(organization_id)');
// Select orgs that were not in the above list.
Organization::whereNotIn('id', $uniqueOrganizationIDs)->get();
This is the solution I came up with:
Query the Organization and Interaction models using list(). For Orgs, get their ID. For Interactions, get their organization_id. I figure these are two low-footprint, fast queries.
Do an array_diff() on them to get an array of Organizations that don't have Interactions.
Query Organization using where_in(), feeding it the diff'ed array.
It looks like this:
$organizationIDs = DB::table('organizations')->where('is_deleted', '=', 0)->lists('id');
$interactionIDs = DB::table('interactions')where('is_deleted', '=', 0)->lists('organization_id');
$uncontactedOrganizationIDs = array_diff($organizationIDs, $interactionIDs);
$uncontactedOrganizations = Organization::where_in('id', $uncontactedOrganizationIDs)->order_by('created_at', 'DESC')->get();
Is there a better way to do this? I feel like there has to be.

Logical Column in MySQL - How?

I have a datamodel, let's say: invoices (m:n) invoice_items
and currently I store the invoice total, calculated in PHP by totalling invoice_items, in a column in invoices. I don't like storing derived data as it paves the way for errors later.
How can I create a logical column in the invoices table in MySql? Is this something I would be better handling in the PHP (in this case CakePHP)?
There's something called Virtual Fields in CakePHP which allows you to achieve the same result from within your Model instead of relying on support from MySQL. Virtual Fields allow you to "mashup" various data within your model and provide that as an additional column in your record. It's cleaner than the other approaches here...(no afterFind() hacking).
Read more here: http://book.cakephp.org/view/1608/Virtual-fields
Leo,
One thing you could do is to modify the afterFind() method in your model. This would recalculate the total any time you retrieve an invoice (costing runtime processing), but would mean you're not storing it in the invoices table, which is apparently what you want to avoid (correct if I'm wrong).
Try this:
class Invoice extends AppModel {
// .. other stuff
function afterFind() {
parent::afterFind();
$total = 0;
foreach( $this->data['Invoice']['InvoiceItems'] as $item )
$total += ($item['cost'] * $item['quantity']);
$this->data['Invoice']['total'] = $total;
}
}
I may have messed up the arrays on the hasMany relationship (the foreach line), but I hope you get the jist of it. HTH,
Travis
Either you can return the derived one when you want it via
SELECT COUNT(1) as total FROM invoice_items
Or if invoices can be multiple,
//assuming that invoice_items.num is how many there are per row
SELECT SUM(num) as total FROM invoice_items
Or you can use a VIEW, if you have a certain way you want it represented all the time.
http://forge.mysql.com/wiki/MySQL_virtual_columns_preview
It's not implemented yet, but it should be implemented in mysql 6.0
Currently you could create a view.