TYPO3 saving Backend content in another database - database

I want to store content from my Backend in another database.
So let's say i have this in my Backend:
How can i save the value (e.g. the float value in the picture) in another database?
The reason why i need this, is, because i have another database, which is being used for some dynamic content loaded onto my Website with PHP.
Hopefully, someone has an idea and can help me :)

I would use either a hook which updates the foreign database with the value which is triggered if something is changed in the TYPO3 backend or I would use a scheduler task / command controller which is triggered by CLI and runs all x minutes and changes the values in the database.

There are several ways to achieve that. I just assume that you want to create relations between the tt_content table of TYPO3 and some external table in a different database or even different storage engine (web-service, file-system, ...).
In that case you could extend the TCA of the tt_content table by an additional property, let's call it external_reference. The backend form then should provide an additional selector field that allows to chose entities of the external data-source.
The following example assumes that your extension key is called my_extension, this has to be adjusted of course to the actual naming.
You can do so by putting the following configuration to your extension in the folder typo3conf/ext/my_extension/Configuration/TCA/Overrides/tt_content.php:
<?php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns(
'tt_content',
[
'external_reference' => [
'exclude' => 1,
'label' => 'External Source',
'config' => [
'type' => 'select',
'items' => [
['-- none --', 0]
],
'itemsProcFunc' => ExternalReferenceSelection::class . '->render',
'default' => 0,
]
],
]
);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes(
'tt_content',
'external_reference'
);
Then you have to implement the selector and the retrieval from the external source, like e.g.
<?php
class ExternalReferenceSelection
{
public function render(array $parameters)
{
$references = ExternalReferenceRepository::instance()->findAll();
foreach ($references as $reference) {
$parameters['items'][] = [
$reference->getTitle(),
$reference->getIdentifier()
];
}
}
}
To be able to persist the selected reference, you have to extend the SQL schema of tt_content as well in typo3conf/ext/my_extension/ext_tables.sql
#
# Table structure for table 'tt_content'
#
CREATE TABLE tt_content (
external_reference int(11) unsigned DEFAULT '0' NOT NULL
);
The database schema is updated by invoking the database analyzer in the TYPO3 Install Tool, or by (re-)installing the extension in the Extension Manager.

Related

What's going wrong with my migrations on CakePHP 4?

I've applied a couple of minor changes to the database structure for my app, adding new columns to a table called Plots. This is one of the migrations -
declare(strict_types=1);
use Migrations\AbstractMigration;
class AddGarageToPlots extends AbstractMigration
{
public function change()
{
$table = $this->table('plots');
$table->addColumn('garage', 'string', [
'default' => null,
'limit' => 255,
'null' => true,
]);
$table->update();
}
}
When I apply the migration it seems to run fine: there are no errors and I can see the new column in the database if I connect directly to it but when I try to access data in the new field in a view using, for example, <?= $plot->garage ?> it consistently returns null even though I have populated this field via the direct connection.
Is there something else I need to do that I'm missing here or is there some way I can check that the migration has worked properly like a schema file somewhere?
Found the answer to my own question by reading slightly further in the documentation - migrations and deployment.
I needed to run bin/cake schema_cache clear

setTable not working as expected with a legacy database in CakePHP 3.x

I have a legacy (not written to Cake naming conventions) database on a CakePHP 3.5.13 application.
One of the database tables is named article_95.
When I attempted to bake the application it's showing the entity name as Article95. It's then producing loads of error messages saying:
Table 'article95' doesn't exist
So I've read CakePHP error: cake bake is using the wrong table name and How to use table name different then in database in cake php 3 and decided to do it manually using setTable().
So I have created src/Model/Table/Article95Table.php with the following code in it:
namespace App\Model\Table;
use Cake\ORM\Table;
class Article95Table extends Table
{
public function initialize(array $config)
{
$this->setTable('article_95');
}
}
But it won't seem to recgonise this. In a controller I've created a testing method and done the following:
Cache::clear(false);
$Article95 = TableRegistry::get('Article95');
debug($Article95->find()->list());
But it's coming up with:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'article95' doesn't exist
It's as if it's not reading the setTable() method.
I've used the Cache::clear(false) to ensure it's not being cached and have inspected the tmp/cache directory for any suspect files - nothing relevant in there.
Any ideas?
Equally if I change my debug() statement to just debug($Article95) I get the following, with the wrong table name:
object(Cake\ORM\Table) {
'registryAlias' => 'Article95',
'table' => 'article95',
'alias' => 'Article95',
'entityClass' => '\Cake\ORM\Entity',
'associations' => [],
'behaviors' => [],
'defaultConnection' => 'default',
'connectionName' => 'default'
}
Incidentally, if I put a non-existent class/entity name in TableRegistry::get() - for example TableRegistry::get('dsfdsfdsfsd'); it won't produce an error message but will show the object above with the non-existent table name. Surely this is also wrong?
I've managed to get this working so wanted to share my solution.
After some trial and error it seems that appending an 's' to the class name (presumably to make Cake consider it plural?) works.
So I renamed the Table class to Article95sTable.php and then used the following code inside:
class Article95sTable extends Table
{
public function initialize(array $config)
{
$this->setTable('article_95');
}
}
Now when I call the following it picks up the table correctly:
$foo = TableRegistry::get('Article95s');
debug($foo);
To confirm this is working if I rename the table inside my setTable() method the output will change accordingly.

Yii2 + Redis as Database

I want to use Yii2 and redis as database.
So far, I got Redis ActiveRecord Class for Yii2 from Here.
link1
link2
but, I got a problem. WHY THIS CLASS ADDS ANYTHING AS HASH IN REDIS????
Above that I cant Find in which pattern it Insert data. I add one user and it will add a user under user:xxx namespace and another record under s:user:xxx and so on but none of theme has any fields that i defined in attributes!! only contain IDs.
I know that a Key-value type database and RDBMS are different and also know how can implement relation like records in Redis, but I don't know why it will only save IDs.
I could not find any example of using redis ActiveRecords so far.
There is one in here and its not good enough.
So here is my main wuestion: how can add data to redis Using activeRecords and different data types In YII2?
And if its impossible with ActiveRecords what is the best solution? in this case
ANOTHER QUESTION: is it possible to use a Model instead and write my own model::save() method? and what is the best data validation solution at this rate?
Actually I want to make a telegram bot, so i should get messages and send them in RabitMQ and get data in a worker, do the process and save results to Redis, and finally send response to user through the RabitMQ.
So I need to do a lot of validations AND OF COURSE AUTHENTICATIONS and save and select and range and save to sets an lists and this and that ....
I want a good way to make Model or active record or the proper solution to validation, save and retrieve data to Redis and Yii2.
Redis DB can be declared as a cache component or as a database connection or both.
When it is declared as a cache component (using the yii/redis/cache) it is accessible within that component to store key/value pairs as shown here.
$cache = Yii::$app->cache;
// try retrieving $data from cache
$data = $cache->get($key);
// store $data in cache so that it can be retrieved next time
$cache->set($key, $data);
// one more example:
$access_token = Yii::$app->security->generateRandomString();
$cache->add(
// key
$access_token,
// data (can also be an array)
[
'id' => Yii::$app->user->identity->id
'name' => Yii::$app->user->identity->name
],
// expires
60*60*3
);
Also other components may start using it for caching proposes like session if configured to do so or like the yii\web\UrlManager which by default will try to cache the generated URL rules in whatever valid caching mechanism defined under the config file's cache component as explained here. So it is normal to find some stored data other than yours in that case.
When Redis is declared as a DB connection like in the links you provided which means using the yii\redis\Connection class you can make your model extending its \yii\redis\ActiveRecord class as any other ActiveRecord model in Yii. The only difference I know so far is that you need to define your attributes manually as there is no DB schema to parse for NoSQL databases. Then just define your rules, scenarios, relations, events, ... as any other ActiveRecord model:
class Customer extends \yii\redis\ActiveRecord
{
public function attributes()
{
return ['id', 'name', 'address', 'registration_date'];
}
public function rules()
{
return [
['name', 'required'],
['name', 'string', 'min' => 3, 'max' => 12, 'on' => 'register'],
...
];
}
public function attributeLabels() {...}
...
}
All available methods including save(), validate(), getErrors(), ... could be found here and should be used like any other ActiveRecord class as shown in the official guide.

Cake 3: How to add new entity to database with primaryKey set?

I want to populate my database with 'flat' data extracted from an excel sheet. All records are provided as arrays (similar to $request->data) but have their primaryKeys set which values must be kept.
My code:
$imported = 0;
foreach ($data as $record) {
$entity = $table->findOrCreate([$table->primaryKey() => $record[$table->primaryKey()]]);
$entity = $table->patchEntity($entity, $record);
if ($table->save($entity)) {
$imported++;
}
}
The code works, but I'm wondering if there is a better solution?
To clarify: What I want is adding something like
[
['id' => 25, 'title'=>'some title'],
['id'=> 3, 'title' => 'some other title'],
['id' => 4356, 'title' => 'another title']
]
to my empty database. findOrCreate() does the job. But I think it shouldn't be necessary to test every record that it not already exists in database before inserting.
A common problem with records mysteriously losing some of the data being provided to a new Entity is that the Entity does not define the field(s) in question as _accessible.
Cake's BakeShell will skip the primary key fields when generating new Entity classes for you, for example:
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
/**
* Widget Entity.
*/
class Widget extends Entity {
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* #var array
*/
protected $_accessible = [
// `id` is NOT accessible by default!
'title' => true,
];
}
There are a few ways to work around this.
You can modify your Entity class to make the id field permanently assignable:
protected $_accessible = [
'id' => true, // Now `id` can always be mass-assigned.
'title' => true,
];
Or you can adjust your call to newEntity() to disable mass assignment protection:
$entities = $table->newEntity($data, [
'accessibleFields' => ['id' => true],
]);
I've found the most important take-away when you're having issues with Cake 3 DB data is to double-check the Entity as soon as it's created or patched and compare it against your input data. You still need to have a sharp eye, but doing so would reveal that the Entities did not have their ->id property set at all even though $data defined them.
If you really only ever work with empty tables, then you can simply save the data straight away, no need to find and patch, just save with disabled existence check.
Also from looking at your code, the data seems to be in a format that can be turned into entities right away, so you may want to create them all at once.
$entities = $table->newEntities($data, [
// don't forget to restrict assignment one way or
// another when working with external input, for
// example by using the `fieldList` option
'fieldList' => [
'id',
'title'
]
]);
// you may want to check the validation results here before saving
foreach ($entities as $entity) {
if ($table->save($entity, ['checkExisting' => false])) {
// ...
}
// ...
}
See also
Saving Entities
Converting Request Data
Avoiding Property Mass Assignment Attacks
Calidating Data Before Building Entities

Drupal services module JSON response fetched: Backbone.js model attributes turn into string

I've set up web services using Drupal's services module. It outputs JSON for me which I am requesting through a Backbone.js front-end application.
I'm having issues with this set-up. If I request data through Backbone.js' fetch method of a model, the model's attributes are all typed as string after fetching, while there are some attributes that should be e.g. integer.
For example:
I have enabled the user resource, which is standard available in the Drupal services module
I can request a user, e.g.:
http://mydevmachine/services/user/8
...which results in the following response (slimmed down version from the real response):
{"uid":"8","name":"itsme","mail":"me#mydomain.nl"}
What I see in the response from the web service above, all values are quoted, however uid is really not a string but an integer in the database.
If I fetch the same user in my Backbone.js model, by setting the uid field of my model to 8 (integer), then call the fetch method. After fetching the uid field is typed as 'string'.
I assume the above leads to my model ending up with a uid attribute of not integer, but string. It also happens with all other web service resources I have created, using my own entities.
I need correct typing of attributes in my model due to sorting issues using Backbone's collection sorting. I.e. sorting a collection of models using a field of type 'integer' leads to different sorting results when sorting the field with the same values although stored as a string.
I'm not sure exactly where to look:
Is the JSON format output by the Drupal services module according to standards?
Is the JSON output format configurable or overridable in the Drupal services module?
Is it perhaps possible to keep the type of a model's attribute after a fetch in Backbone.js?
Should I provide a specific implementation for Backbone's collection comparator function, which handles this situation (seems hackey)?
Should I introduce other solutions, e.g. like posted here: How can I enforce attribute types in a Backbone model? (feels too heavy).
Thanks for any help.
So I finally managed to crack this issue and I found my solution here: How to get numeric types from MySQL using PDO?. I thought I'd document the solution.
Drupal 7 uses PDO. Results fetched using PDO, using Drupal's default PDO settings result in stringified values.
In Drupal's includes/database.inc file you will find this around lines 40-50:
$connection_options['pdo'] += array(
// So we don't have to mess around with cursors and unbuffered queries by default.
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE,
// Because MySQL's prepared statements skip the query cache, because it's dumb.
PDO::ATTR_EMULATE_PREPARES => TRUE,
);
The statement here that MySQL's prepared statements skip the query cache is not entirely true, as can be found here: http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html. It states MySQL > 5.1.17 prepared statements use the query cache under certain conditions.
I used the info from the other stack overflow question/answers to override the PDO settings for the database connection in Drupal's sites/default/settings.php (please note I only did this for the database I was querying, which is different than Drupal's own database):
'database_name' =>
array (
'default' =>
array (
'database' => 'database_name',
'username' => 'user_name',
'password' => 'user_pass',
'host' => 'localhost',
'port' => '',
'driver' => 'mysql',
'prefix' => '',
'pdo' => array(
PDO::ATTR_STRINGIFY_FETCHES => FALSE,
PDO::ATTR_EMULATE_PREPARES => FALSE
),
),
),
This resulted in integers being integers. Floats/decimals are incorrectly returned by PDO still, but this is different issue. At least my problems are solved now.

Resources