Thanks for advice!
]]>Thats not transparent of course, but thats the goal here clearly ![]()
Fun fact: The new Cake53 pagination system I built also does allow similar grouping and sorting for general cases, you can add inifite things within one “x asc/desc” and make those not exposed.
]]>APP/Database/Driver/CustomSqlserver.php
<?php
declare(strict_types=1);
namespace App\Database\Driver;
use Cake\Database\Driver\Sqlserver;
use PDO;
class CustomSqlserver extends Sqlserver
{
/**
* Override the connect() method
* The main reason to override the connect() method is to add the authentication parameter,
* this is backward compatitable with existing code
*
* @return void
*/
public function connect(): void
{
if ($this->pdo !== null) {
return;
}
$config = $this->_config;
if (isset($config['persistent']) && $config['persistent']) {
throw new InvalidArgumentException(
'Config setting "persistent" cannot be set to true, '
. 'as the Sqlserver PDO driver does not support PDO::ATTR_PERSISTENT',
);
}
$config['flags'] += [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
if (!empty($config['encoding'])) {
$config['flags'][PDO::SQLSRV_ATTR_ENCODING] = $config['encoding'];
}
$port = '';
if ($config['port']) {
$port = ',' . $config['port'];
}
$fqdn = '';
if (count(explode('.', $config['host'])) == 1) {
$fqdn = $config['host'] . '.database.windows.net';
} else {
$fqdn = $config['host'];
}
$dsn = "sqlsrv:Server={$fqdn}{$port};Database={$config['database']};MultipleActiveResultSets=false";
if ($config['app'] !== null) {
$dsn .= ";APP={$config['app']}";
}
if ($config['connectionPooling'] !== null) {
$dsn .= ";ConnectionPooling={$config['connectionPooling']}";
}
if ($config['failoverPartner'] !== null) {
$dsn .= ";Failover_Partner={$config['failoverPartner']}";
}
if ($config['loginTimeout'] !== null) {
$dsn .= ";LoginTimeout={$config['loginTimeout']}";
}
if ($config['multiSubnetFailover'] !== null) {
$dsn .= ";MultiSubnetFailover={$config['multiSubnetFailover']}";
}
if ($config['encrypt'] !== null) {
$dsn .= ";Encrypt={$config['encrypt']}";
}
if ($config['trustServerCertificate'] !== null) {
$dsn .= ";TrustServerCertificate={$config['trustServerCertificate']}";
}
// Custom add this Authentication method
if ($config['authentication'] !== null) {
$dsn .= ";Authentication={$config['authentication']}";
}
$this->pdo = $this->createPdo($dsn, $config);
if (!empty($config['init'])) {
foreach ((array)$config['init'] as $command) {
$this->pdo->exec($command);
}
}
if (!empty($config['settings']) && is_array($config['settings'])) {
foreach ($config['settings'] as $key => $value) {
$this->pdo->exec("SET {$key} {$value}");
}
}
if (!empty($config['attributes']) && is_array($config['attributes'])) {
foreach ($config['attributes'] as $key => $value) {
$this->pdo->setAttribute($key, $value);
}
}
}
}
In app.php
use App\Database\Driver\CustomSqlserver;
'Datasources' => [
'default' => [
'className' => Connection::class,
'driver' => CustomSqlserver::class,
'authentication' => env('DB_AUTHENTICATION', null),
And finally, set DB_AUTHENTICATION in my local environment for testing with a Service Principle, along with other stuff such as hostname
export DB_HOST='<server_name>'
export DB_NAME='<database_name>'
export DB_AUTHENTICATION='ActiveDirectoryServicePrincipal'
In Azure > App Settings > Configuration, you set DB_AUTHENTICATION to ActiveDirectoryMsi
Release bug fixes until September 10 2025 (24 months after 5.0.0).
Release security fixes until December 10 2026 (36 months after 5.0.0).
I can’t find anything on the website - so asking here…
How long will CakePHP v4.x be supported for? Just wanting some ideas of when I should be looking into moving to CakePHP v5.
cheers, Dean
]]>The most interesting thing is that on php 8.4(x64) it works and on php8.4(x86) it doesn’t work.
]]>In TableSchema.php around line 381, there’s likely code like:
new Column($name, $type, $nullable, $default, $length, …)
Where $length comes from database metadata and isn’t being cast to int.
]]>I used for this test:
OS: windows
CakePhp: 5.3.1 (with debugkit activated)
Php: 8.4 (x86)
When open default page I have following message:
Cake\Database\Schema\Column::__construct(): Argument #5 ($length) must be of type ?int, float given, called in \vendor\cakephp\cakephp\src\Database\Schema\TableSchema.php on line 381
But your “problem” here is this line at the top:
$appnews = $this->get('appNews');
You don’t need to retrieve data inside your templates. Whenever you set a variable inside the controller like so
$this->set(compact('news'));
you automatically have access to it via `$news`
Also you can look at all your accesible variables via Debug Kit “Variables” Panel or just add a simple
pr($this->viewVars);
in your template (and make sure your Debug Mode is active) to see what your current scope of variables has to offer.
]]>public function index($id = null) {
$query = $this->News->find();
$news = $this->paginate($query, [
'scope' => 'news'
]);
$this->set(compact('news'));
$this->render('/Homepage/News/index');
}
Error: TypeError: Cake\View\View::get(): Argument #1 ($var) must be of type string, int given
called in PaginatorHelper.php on line 142
Template
<?php
/**
* @var \\App\\View\\AppView $this
* @var iterable<\\App\\Model\\Entity\\Article> $articles
*/
$yesno = $this->get('appYesNo');
$appnews = $this->get('appNews');
?>
<?= $this->Element('navigation', ['navi' => $prodItem]); ?>
<div class="news index content">
<?= $this->Html->link(__('Neue Neuigkeit'), ['action' => 'add'], ['class' => 'button float-right']) ?>
<h3><?= __('Neuigkeiten') ?></h3>
<div class="table-responsive">
<table>
<thead>
<tr>
<th width="40"><?= $this->Paginator->sort('id') ?></th>
<th width="300"><?= $this->Paginator->sort('title') ?></th>
<th><?= $this->Paginator->sort('type') ?></th>
<th><?= $this->Paginator->sort('folder') ?></th>
<th><?= $this->Paginator->sort('active') ?></th>
<th><?= $this->Paginator->sort('login') ?></th>
<th width="200" class="actions"><?= __('Actions') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($news as $news): ?>
<tr>
<td><?= h($news->title) ?></td>
<td>
<?php
echo $appnews[$news->type];
?>
</td>
<td><?= h($news->folder) ?></td>
<td>
<?php
echo $yesno[$news->active];
?>
</td>
<td>
<?php
echo $yesno[$news->login];
?>
</td>
<?= $this->Element('viewArticleAction', array('recCon' => 'News', 'action' => 'singleNews', 'recId' => $news->id, 'parentId' => $news->id)); ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="paginator">
<ul class="pagination">
<?= $this->Paginator->first('<< ' . __('first')) ?>
<?= $this->Paginator->prev('< ' . __('previous')) ?>
<?= $this->Paginator->numbers() ?>
<?= $this->Paginator->next(__('next') . ' >') ?>
<?= $this->Paginator->last(__('last') . ' >>') ?>
</ul>
<p><?= $this->Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?></p>
</div>
</div>
Whats the problem? Kid regards Klaus
]]>I just wanted to clear things up so you understand, that PHP is not a “compiled” language like Java is.
If you have any problems feel free to just post it in here or go to our slack or discord support channel.
]]>I did post yesterday to say I’d managed to get it working. I would have marked it as resolved but for some reason I couldn’t edit the original post, only my additional comment where I said I’d fixed it.
Re your comment about Netbeans, I’ve been using it as a debugger for a few years and it works well on a local web application, as one might expect as it comes from “Friends of Apache”.
I’m sorry if my use of the word “start” offends you, I consider the web app has “started” when the home page appears, which I tried to indicate in my original post. I came to web development late in life, I’m afraid I predate the internet. Now at the grand old age of 80 perhaps my vocabulary differs somewhat from yours ![]()
I’d recommend you take a look at Installation Guide | CakePHP and learn about how webservers work and what a “docroot” is.
Unless you use bin/cake server (or Netbeans calls that via your project settings), then its the PHP Built In Webserver.
]]>No changes the the netneans project settings, I can go back to the old 4.4.17 project & it works fine.
I feel I must be missing something but I know not what.
Has anyone seen this or can throw any light on it?
tia
Peter
]]>What I wnat to do is searching different values in different fields, eg email = [email protected] and role_id = 1 and status_id = 2
select * from users where [email protected] or role_id = 1 or status_id = 2
If you REALLY want OR search instead of AND (which is weird, but ok), then you probably need the custom callback here:
->add('q', 'Search.Callback', [
'callback' => function ($query, $args) {
$value = $args['q'] ?? null;
if ($value === null) {
return $query;
}
return $query->where([
'OR' => [
'column1' => $value,
'column2' => $value,
],
]);
},
]);
for the same fields.
In your case, with 3 different values, probably more like
->add('q', 'Search.Callback', [
'callback' => function ($query, $args) {
$or = [];
if (!empty($args['col1'])) {
$or[] = ['column1' => $args['col1']];
}
if (!empty($args['col2'])) {
$or[] = ['column2' => $args['col2']];
}
if (!empty($args['col3'])) {
$or[] = ['column3' => $args['col3']];
}
return $or ? $query->where(['OR' => $or]) : $query;
},
]);
This yields column1 = val1 OR column2 = val2 OR column3 = val3, each with its own value.
But usually searches are AND combined, so normal value() and like() filters should be totally sufficient.
]]>With the class itself you are more flexible in general, and reusable for other actions if needed.
As for your concrete issue:
Why are you not using value() on role? after all, you are doing the same here as for user_id/status_id etc (direct value match).
in my searchManager() function in my UserTable I put in there
$this->getBehavior('Search')->searchManager()
->value('user_id')
->add('email', 'Search.Like', [
'before' => true,
'after' => true,
'fieldMode' => 'OR',
'comparison' => 'LIKE',
'wildcardAny' => '*',
'wildcardOne' => '?',
'fields' => ['email'],
])
This will find user by email. But when I add there a second field like role_id
$this->getBehavior('Search')->searchManager()
->value('user_id')
->add('email', 'Search.Like', [
'before' => true,
'after' => true,
'fieldMode' => 'OR',
'comparison' => 'LIKE',
'wildcardAny' => '*',
'wildcardOne' => '?',
'fields' => ['email', 'role_id'],
])
Then I get an error because this tries to search the email also in the field role_id, which is a number.
and when I add another field
$this->getBehavior('Search')->searchManager()
->value('user_id')
->add('email', 'Search.Like', [
'before' => true,
'after' => true,
'fieldMode' => 'OR',
'comparison' => 'LIKE',
'wildcardAny' => '*',
'wildcardOne' => '?',
'fields' => ['email'],
])
->add('role_id', 'Search.Like', [
'before' => false,
'after' => false,
'fieldMode' => 'AND',
'comparison' => 'LIKE',
'wildcardAny' => '*',
'wildcardOne' => '?',
'fields' => ['role'],
]);
The role_id will be ignored on the search query.
What I found in docu was that I can use the add() methode several times. But it does not work in my example.
In additional, I do not know, what else can I use instead of like search. I want to find where role_id = 2
Sorry but when I read docu, I have a lot of questions
]]>Well then its up to what you define as domain objects
I’m trying to go with the standard definition here, which is the model that describes a key aspect of my business domain.
One way would be to create a seperate model in ie. /domain, and then using the data from the entity to run the actual business logic on it. That way, the CakePHP models are not abused for domain matters and the domain stays free of framework logic.
Interesting. I thought it was the other way round, table classes being just “dumb” persistence models (hence the Table suffix).
Btw. the doc also states:
]]>entities represent individual rows or domain objects in your application
An Invoice with 0 line items makes little sense. How can I catch this?
What you are looking for is Application Rules
Those are applied when you try to save an entity (and maybe associated entities as well)
As the docs say
rules focus on comparing data against the existing state of your application and/or network.
So your entity state can/must be wrong as it needs to hold the wrong state, but the check logic is being done by the table instance.
If application rules don’t pass the saving process fails.
What you expect is, that validation happens the moment an entity gets state injected into it, which is not what Cake is built upon.
Entities are dumb objects which just repesent state (or desired state) in a row/in the db
Table objects do the logic and validation.
]]>I use CakePHP’s entities as domain models mostly, where I keep the business rules. For those rules to apply correctly, it is important that the object is in a valid state, ie. `status` must be a `InvoiceStatus` enum. The way it is, I must pass an array of values when I instantiate the object:
new Invoice([ 'status' => 'notWhatIexpected' ]);
Invoice is created with an invalid property.
Same applies to `LineItem`, which belongs to `Invoice`. An Invoice with 0 line items makes little sense. How can I catch this? I was thinking about a static creation method, but what is the CakePHP way?
Copilot’s suggestion is to check all values manually before instantiating the invoice, but this doesn’t seem right. Because the validity of the Invoice model is a kind of business rule in itself (“An invoice status can be draft or final”). Therefore I want to encapsulate this logic in the Invoice model.
Regards
]]>I downloaded the search plugin and I tried to get it running, reading docu.
But for now it looks like, I can search 1 search element in different fields of a table.
What I wnat to do is searching different values in different fields, eg email = [email protected] and role_id = 1 and status_id = 2
select * from users where [email protected] or role_id = 1 or status_id = 2
But I am not sure how to configure the search plugin
]]>See e.g.
for a live demo implementation.
]]>I list all users from database but now I want to filter either by email, or by role or by status.
I tried to use the following option for retrieving Data: Dynamic Finders
But when I try to use
$this->Users->findAllByEmailOrRoleOrStatus($email, $role, $status);
and one of the fields is not set, the I get an error.
Expression `Users.email` has invalid `null`
How can I use this dynamically? Some ideas?
Cheers Frank
]]>All I have to say is, thanks for your effort creating this video. It helped me a lot.
cheers Frank
]]>What you need to do is add your “not logged in actions” to be called without a user.
Look at CMS Tutorial - Authentication | CakePHP and search for addUnauthenticatedActions
You can also check out my auth workshop from 3 years ago (but still valid!)
]]>* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
correct
* @method \App\Model\Entity\User[]|\Cake\Datasource\Paging\PaginatedResultSet paginate($object = null, array $settings = [])
]]>