I have a site where I want to be able to link to the next ProductPage from the ProductPage I am currently on. I can do this for immediate siblings, but once I get to the end of that set I want to link to the next ProductRange first product. The structure is like this:
ProductArea
ProductCategory
- ProductRange
- ProductPage
- ProductPage
- ProductRange
- ProductPage
- ProductPage
ProductCategory
- ProductRange
- ProductPage
- ProductPage
- ProductRange
- ProductPage
- ProductPage
So if there is no direct sibling I need to return the next product page in order.
I'm using this for direct siblings:
function NextProduct() {
$pages = ProductPage::get()->filter(array(
'ParentID' => $this->ParentID,
'Sort:GreaterThan' => $this->Sort
))->Sort('Sort');
if ($pages) {
return $pages->First();
}
}
How do I get it working at the next level up?
Here is one way to do this. First we try to find the next sibling. If the next sibling does not exist we get the next parent page and get their first child.
function NextProduct() {
$pages = ProductPage::get()->filter(array(
'ParentID' => $this->ParentID,
'Sort:GreaterThan' => $this->Sort
))->Sort('Sort')->limit(1);
if ($pages->count()) {
return $pages->First();
}
$parent = $this->parent();
$parentSiblings = ProductRange::get()->filter(array(
'ParentID' => $parent->ParentID,
'Sort:GreaterThan' => $parent->Sort
))->Sort('Sort');
foreach ($parentSiblings as $parentSibling) {
$pages = ProductPage::get()->filter(array(
'ParentID' => $parentSibling->ID
))->Sort('Sort')->limit(1);
if ($pages->count()) {
return $pages->First();
}
}
return false;
}
Here is a PreviousProduct function:
function PreviousProduct() {
$pages = ProductPage::get()->filter(array(
'ParentID' => $this->ParentID,
'Sort:LessThan' => $this->Sort
))->Sort('Sort')->reverse()->limit(1);
if ($pages->count()) {
return $pages->First();
}
$parent = $this->parent();
$parentSiblings = ProductRange::get()->filter(array(
'ParentID' => $parent->ParentID,
'Sort:LessThan' => $parent->Sort
))->Sort('Sort')->reverse();
foreach ($parentSiblings as $parentSibling) {
$pages = ProductPage::get()->filter(array(
'ParentID' => $parentSibling->ID
))->Sort('Sort')->reverse()->limit(1);
if ($pages->count()) {
return $pages->First();
}
}
return false;
}
Related
I create Custom Token and I want to get Webform Element and assign it to the Token, in my case the Element that I try to return is IP_ADDRESS , I tried to do the below and it didn't work:
use Drupal\webform\WebformSubmissionInterface;
function MODULE_token_info() {
// token info
}
function MODULE_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata, WebformSubmissionInterface $webform_submission = NULL) {
$ip = $webform_submission->getRemoteAddr();
$replacements = [];
if ($type == 'custom') {
foreach ($tokens as $name => $original) {
switch ($name) {
case 'order-cart':
$replacements[$original] = t('First Value');
break;
case 'city-token':
$replacements[$original] = getCity($ip);
break;
}
}
}
return $replacements;
}
I'm sure the $ip var is not the right way to retrieve Element value. also I don't wanna use the $_SERVER['REMOTE_ADDR'], I need to get the Submission Ip Address.
I'll appreciate the help, thanks
In wpDataTables, I would like to modify (i.e. conditionally format) each cell value in a specific column for a specific table programmatically using PHP. How would I accomplish this?
First, install the Code Snippets plugin. Then create a new snippet set to "Run Snippet Everywhere" (required for JSON filtering) using the code below. It will filter both HTML and JSON. For more information, refer to wpDataTables - Filters.
function custom_wpdatatables_filter_initial_table_construct($tbl) {
// Edit below.
$table_name_to_modify = 'My Table Name';
$table_column_to_modify = 'my_table_column';
$cell_modification_function = function($value) {
return 'Modified: ' . $value;
};
// Check table name.
if ($tbl->getName() !== $table_name_to_modify) {
return $tbl;
}
$rows = $tbl->getDataRows();
foreach ($rows as &$row) {
if (array_key_exists($table_column_to_modify, $row)) {
$row['intermentobituary'] = $cell_modification_function($row['intermentobituary']);
}
}
$tbl->setDataRows($rows);
return $tbl;
}
add_filter('wpdatatables_filter_initial_table_construct', 'custom_wpdatatables_filter_initial_table_construct', 10, 1);
function custom_wpdatatables_filter_server_side_data($json, $tableId, $get) {
// Edit below.
$table_name_to_modify = 'My Table Name';
$table_column_to_modify = 'my_table_column';
$cell_modification_function = function($value) {
return 'Modified: ' . $value;
};
// Check table name.
$tableData = WDTConfigController::loadTableFromDB($tableId);
if (empty($tableData->content)) {
return $json;
} else if ($tableData->title !== $table_name_to_modify) {
return $json;
}
// Get columns.
$columns = [];
foreach ($tableData->columns as $column) {
// wdt_ID will be first column.
$columns[] = $column->orig_header;
}
// Modify column values.
$json = json_decode($json, true);
$rows = $json['data'];
foreach ($rows as $row_key => $row_value) {
foreach ($row_value as $row_attr_key => $row_attr_value) {
if ( ! empty($columns[$row_attr_key]) && $columns[$row_attr_key] === $table_column_to_modify) {
$rows[$row_key][$row_attr_key] = $cell_modification_function($row_attr_value);
}
}
}
$json['data'] = $rows;
return json_encode($json);
}
add_filter('wpdatatables_filter_server_side_data', 'custom_wpdatatables_filter_server_side_data', 10, 3);
I have an Entity named Page that can be a callToAction (boolean) and I would like to display the Page Entity with callToAction == false on one subMenu and the Page Entity with callToAction == true on another subMenu. I have a CRUD for the Page Entity. So the Dashboard would be something like that:
MenuItem::subMenu('Page', 'far fa-file-alt')->setSubItems([
MenuItem::linkToCrud('Page', 'fa fa-alt', Page::class),
MenuItem::linkToCrud('Call To Action', 'fa fa-file-alt', Page::class),
])
But I don't know where to put the dql to display the entities I want (callToAction true or false) and I don't even know if it's possible, but I know it was with Easy Admin 2, that's why I wonder.
I also would like that on the NEW Action, when you're on the Page with callToAction == true, when you create the new Entity Page from here, that the callToAction is set to true immediatly and the User doesn't even see the field. Still don't know if it's possible.
Thanks :)
EDIT: So i've found that I can use createIndexQueryBuilder() to display on the index exactly the entities, and it works well but I don't know how to call two different createIndexQueryBuilder depending of the subMenu we display. I tried doing a custom action and using createQueryBuilder but I don't have the params searchDto, etc:
public function configureActions(Actions $actions): Actions
{
$indexIsCallToAction = Action::new('indexIsCallToAction', 'Index Call To Action', 'fa fa-send')
->linkToCrudAction('indexIsCallToAction');
$actions->add(CRUD::PAGE_INDEX, $indexIsCallToAction);
return $actions;
//return parent::configureActions($actions); // TODO: Change the autogenerated stub
}
public function indexIsCallToAction(AdminContext $context,SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters){
$response = $this->get(EntityRepository::class)->createQueryBuilder($searchDto, $entityDto, $fields, $filters);
return $response;
}
So it doesn't work.
As a dashboard controller is an ordinary controller you can do something like this:
public function __construct(PageRepository $pageRepo)
{
$this->pageRepo = $pageRepo;
}
public function configureMenuItems(): iterable
{
$submenuItems = [];
if (null !== $pages = $this->pageRepo->findBy(["callToAction" => true ])) {
foreach ($pages as $page) {
$submenuItems[] = MenuItem::linkToCrud('Call To Action', 'fa fa-file-alt', Page::class);
}
}
yield MenuItem::subMenu('Page Submenu with callToAction', 'far fa-file-alt')->setSubItems($submenuItems);
$submenuItems = [];
if (null !== $pages = $this->pageRepo->findBy(["callToAction" => false ])) {
foreach ($pages as $page) {
$submenuItems[] = MenuItem::linkToCrud('Page', 'fa fa-alt', Page::class);
}
}
yield MenuItem::subMenu('Other Page Submenu', 'far fa-file-alt')->setSubItems($submenuItems);
}
I have an application which allows users to create wishes. I use the title of each wish to make an api request to unsplash to download a picture. I now have the problem, that a user can enter a title which doesnt return any images from unsplash. In this case I'd like to use a placeholder image but my code stops after getting an 404 error. Is there a way to ignore this error and just continue my loop?
public function fetchImagesFromUnsplash() {
$wishes = $this->repository->findAll();
foreach ($wishes as $wish) {
try {
$response = $this->httpClient->request('GET', 'https://api.unsplash.com/photos/random', [
'query' => [
'query' => $wish->getDescription(),
'client_id' => 'oa1DsGebE8ehCV9SrvcA1mCx-2QfvnufUKgsIY5N0Mk'
]
]);
} catch (TransportExceptionInterface $e) {
}
if ($response) {
$data = $response->getContent();
$data = json_decode($data, true);
$imageLink = $data['urls']['raw'];
$rawImage = file_get_contents($imageLink);
if ($rawImage) {
file_put_contents("public/images/" . sprintf('imageWish%d.jpg', $wish->getId()), $rawImage);
$wish->setImagePath(sprintf('public/images/imageWish%d.jpg', $wish->getId()));
} else {
$wish->setImagePath('placeholder.png');
}
$this->em->flush();
}
}
}
EDIT:
I tried this:
public function fetchImagesFromUnsplash() {
$wishes = $this->repository->findAll();
foreach ($wishes as $wish) {
try {
$response = $this->httpClient->request('GET', 'https://api.unsplash.com/photos/random', [
'query' => [
'query' => $wish->getDescription(),
'client_id' => 'oa1DsGebE8ehCV9SrvcA1mCx-2QfvnufUKgsIY5N0Mk'
]
]);
} catch (NotFoundHttpException $e) {
}
if ($response) {
$data = $response->getContent();
$data = json_decode($data, true);
$imageLink = $data['urls']['raw'];
$rawImage = file_get_contents($imageLink);
if ($rawImage) {
file_put_contents("public/images/" . sprintf('imageWish%d.jpg', $wish->getId()), $rawImage);
$wish->setImagePath(sprintf('public/images/imageWish%d.jpg', $wish->getId()));
} else {
$wish->setImagePath('placeholder.png');
}
}
}
$this->em->flush();
}
but it still stops after the first 404
As per the documentation:
When the HTTP status code of the response is in the 300-599 range
(i.e. 3xx, 4xx or 5xx) your code is expected to handle it. If you
don't do that, the getHeaders() and getContent() methods throw an
appropriate exception
You have to check the $response->getStatusCode(), or prepare to handle a ClientException (representing 4xx status codes).
I parse my xml with Symfony's Crawler and cannot get how can I pass (other words continue) an element and not to include it into final array?
For example:
$node->filterXPath('//ExampleNode')->each(function(Crawler $child, $i) {
if (! count($child->filterXPath('//ChildNode'))) {
continue;
}
return $child->filterXPath('//ChildNode')->text();
});
You can use the Symfony\Component\DomCrawler\Crawler::reduce(Closure)
$crawler->reduce(function($result, $item) {
$childNodes = $item->filterXPath('//ChildNode');
if ($childNodes->count()) {
$result[] = $item;
}
});