How do I build this complex database query in CakePHP 3.0? - cakephp

This is my database design, or at least the tables that are relevant to this question.
I want to build a query that returns a single page (find will be based on the path attribute), with the associated container, with its associated child-containers in threaded form and all of those containers should individually have their associated blocks with them (preferably in the right order sorted by the index column from the blocks_pages table).
Can anybody give me a clue how to wrap that all up with the query-builder? Or if that is not possible, then is it possible to do it using the new map/reduce feature, since the after-find function has been removed?
In case it helps, this will be the visualized result, if you just ignore the magenta Article-box for a moment.

Try this
$pagesTable
->find()
->where(['path' => $myPath])
->contain([
'Containers.ChildContainers' => function($q) {
return $q->formatResults(function($results) {
return $results->map(function($container) {
$container->nested = $container->source()
->find('children', ['for' => $container->id])
->find('threaded')
->contain(['Blocks']);
return $container;
});
});
},
'Containers.ChildContainers.Blocks'
])

Related

Create database seeder with same random value in a table row with Laravel database seeder

I am trying to create a table and populate the table with the following fields with the help of database seeder:
option a
option b
option c
option d
correct option
First four fields will be assigned random word, and the last field 'correct option' will contain any one of the first four.
I could not find any solution to do it with Laravel database seeder. Can anyone help?
Something like this?
use faker random element function in your factory or seeder.
$optionA = $faker->word;
$optionB = $faker->word;
$optionC = $faker->word;
$optionD = $faker->word;
return [
'option_a' => $optionA,
'option_b' => $optionB,
'option_c' => $optionC,
'option_d' => $optionD,
'correct_option' => $faker->randomElement([$optionA,$optionB,$optionC,$optionD]),
];
Create a factory and use Faker to generate the random words you're after
This sounds like an ideal use case for JSON columns (both for questions and answers). For instance, you might decide to have multiple valid answers to a single multiple choice question.
In your migration:
// create_questions_table.php
...
$table->json('choices')->default(new Expression('(JSON_ARRAY())'));
$table->json('answer')->default(new Expression('(JSON_ARRAY())'));
From https://laravel.com/docs/7.x/migrations#column-modifiers:
Using an Expression instance will prevent wrapping the value in quotes and allow you to use database specific functions. One situation where this is particularly useful is when you need to assign default values to JSON columns.
Then create a factory:
// QuestionFactory.php
$factory->define(Location::class, function (Faker $faker) {
$choices = $faker->words(4);
$answer = [ $choices[rand(1,4)] ];
return [
'choices' => $choices,
'answer' => $answer,
];
});
Using the Faker library included in Laravel, we can pick 4 words and randomly assign one of them to be the answer.

How to sort when also using a where clause

I am currently pulling a list from a database, using the following code. The list is retrieved using a WHERE condition, however the list is returned unsorted. This is in the controller.
How can I modify this code so that the returned list is sorted alphabetically?
if (!string.IsNullOrEmpty(TargetYear))
{
ViewBag.HSID = new SelectList(db.Hotspots.Where(g => g.HSID.Contains(TargetYear)).ToList(), "ID", "HSID");
}
On several other fields I have used the following method to order, but I'm not sure how, or if I can combine this with the where clause above. The key piece is ".OrderBy(e=>e.FIELD), however this is precisely the piece I'm not sure how to integrate with the query.
ViewBag.LocalityCode = new SelectList(db.Localities.OrderBy(e=>e.LOCALITY1), "LOC_CODE", "LOCALITY1");
Other helpful bits of info:
ASP.Net MVC5
Microsoft SQL 2012
if (!string.IsNullOrEmpty(TargetYear))
{
var data =
db.Hotspots
.Where(g => g.HSID.Contains(TargetYear))
.OrderBy(e=>e.HSID)
.ToList();
ViewBag.HSID = new SelectList(data,"ID", "HSID");
}

I have a few questions about Join in Cakephp

Thank you for reading it.
I'm studying cakephp and have a few questions about Join in Cakephp.
I coudn't find any appropriate answer on internet
1, What is difference between innerjoin and innerjoinwith (like leftjoin and leftjoinwith) I thought It is related with performence issue, but couldn't find any clue.
2, I can't get the exact difference between "matching" and "contain"
I know the way they retrieving data is different, but I thought the result looks same as the result of matching and matching is using innerjoin, and contain is using leftjoin.
but but I can't find what is the difference between the code below(using contain) and matching.
$query = $articles->find()->contain([
'Comments' => function ($q) {
return $q
->select(['body', 'author_id'])
->where(['Comments.approved' => true]);
}
]);
3, what is "_matchingData" in matching?
I read this description, that is
"The innerJoinWith() method works the same as matching(), that means that you can use dot notation to join deeply nested associations: Again, the only difference is that no additional columns will be added to the result set, and no _matchingData property will be set."
but I couldn't find any description about _matchingData. even in API... what it is?
you can response one of them, it is okay. please help me out
1, What is difference between innerjoin and innerjoinwith (like leftjoin and leftjoinwith) I thought It is related with performence issue, but couldn't find any clue.
=>
(1) here 'innerJoinWith' is a callable which will apply right/inner join
(2) for left join 'leftJoinWith' is the callable
https://book.cakephp.org/3.0/en/orm/query-builder.html#using-innerjoinwith
https://book.cakephp.org/3.0/en/orm/query-builder.html#using-leftjoinwith
2, I can't get the exact difference between "matching" and "contain"
I know the way they retrieving data is different, but I thought the result looks same as the result of enter code here matching and matching is using innerjoin, and contain is using leftjoin.
but but I can't find what is the difference between the code below(using contain) and matching.
$query = $articles->find()->contain([
'Comments' => function ($q) {
return $q
->select(['body', 'author_id'])
->where(['Comments.approved' => true]);
}
]);
=> In short contain applies leftJoin and matching apply right/inner join to the query.
please read this first line on this link https://book.cakephp.org/3.0/en/orm/query-builder.html#using-innerjoinwith
3, what is "_matchingData" in matching?
I read this description, that is
"The innerJoinWith() method works the same as matching(), that means that you can use dot notation to join deeply nested associations: Again, the only difference is that no additional columns will be added to the result set, and no _matchingData property will be set."
but I couldn't find any description about _matchingData. even in API... what it is?
=> _matchingData() is an entity property created when matching() is used. It contains data which is selected matching
$query = $articles->find();
$query = $query->matching('Comments', function($q){
$q->select(['Comments.body', 'Comments.author_id']);
$q->where(['Comments.is_approved' => 1]);
return $q;
});
Above query will have _matchingData property contained with Comments.body, Comments.author_id

Cakephp 3.0: Multiple matching conditions

I'm trying to daisy-chain matching() calls on a query, or find an equivalent way to achieve the same result. Specifically, here's what I'd like to do:
$matchedItemVersion = $this->ItemVersions->find()
->where(['version' => $verion])
->matching('Items', function($q) use ($ipnCore) {
return $q->where(['ipn_core' => $ipnCore]);
})
->matching('Items.ItemTypes.ItemSuperGroups', function($q) use ($itemSuperGroupName) {
return $q->where(['name' => $itemSuperGroupName]);
})
->first()
->toArray();
This would let me filter my ItemVersions results by two conditions on associated models. However, right now, the second matching() call basically overrides the first one, so the first matching() call has no effect on the query.
We know that you cannot chain a matching() and a contain()(https://github.com/cakephp/cakephp/issues/5109), but is there a way to do it with matching(), or a way to get the same result?
Appreciate the help.

How use caching to improve the performance of a classified website?

I have built classified website using Yii php framework. Now it is getting a lot of traffic. So I want to using caching to optimize the performance of the website.
There are two controllers I want to optimize.
One is the thread list controller: (example) http://www.shichengbbs.com/category/view/id/15
The other one the the thread controller: (example) http://www.shichengbbs.com/info/view/id/67900
What I have done:
the thread list is cached for 3mins.(The other option is update the thread list only when new thread comes)
set the last-modified time HTTP header for the thread view. (expire time is not set, as some user complain that the page appears unchanged after editing)
Partial caching the categories navigation fragment.(It appears on the left side of every page)
Use htaccess to set expire header for img/html/css/js.
Considered database sql caching for the thread list, but not done. As I thought it is the same as 1.
What else can I do to improve the website performance?
I assume you have done the Performance Tuning guide point 1 and 3. It's really helpful.
For number 2 you can use the CHttpCacheFilter
class CategoryController extends Controller {
private $_categoryLastUpdate;
public function filters(){
return array(
array(
'CHttpCacheFilter + view',
'cacheControl' => " max-age=604800, must-revalidate",
'etagSeedExpression' => function() {
return $this->getCategoryLastUpdate();
}
'lastModifiedExpression' => function() {
return $this->getCategoryLastUpdate();
}
)
)
}
public function actionView($id){
$object = Category::model()->findByPk($_GET['id']);
$this->render('view', array('object' => $object));
}
public function getCategoryLastUpdate(){
if (!isset($this->_categoryLastUpdate)){
$obj = Category::model()->findByPk($_GET['id'], array('select' => 'lastUpdate'));
$this->_categoryLastUpdate
}
return $this->_categoryLastUpdate;
}
}
It basically will calculate the ETag and LastUpdate by the category. And to save the query, it will first only calculate the lastUpdate of the Category object.
And for number one, you can always use the CCacheDependency. Just make a field in the thread list object, e.g. lastUpdate. And when a new thread submitted, just update the field and use it for the CCacheDependency.
Since I see you are using a very large pagination, I think you want to read about Four Ways to Optimize Paginated Displays (if you use MySQL for your database and thread search/list).
Try using a Cache Manager with Memcache or APC. For example, http://code.google.com/p/memcache-flag/ . When you edit the list, then you can invalidate the cache item or tag. I suppose it could also just be done with regular APC / Memcache functions if you design is simple (set a key and delete it when it is no longer valid).
Use this to store serialized (or automatically serialized) data instead of retrieving it from mysql.

Resources