Woocommerce Booking category view - woocommerce-bookings

Is it possible to display the Woocommerce Booking form (same on the single product page) on the category pages below each product?
I've tried this
add_action( 'woocommerce_after_shop_loop_item', 'custom_booking', 10 );
function custom_booking() {
global $product;
$booking_form = new WC_Booking_Form($product);
$booking_form->output();
}
or
add_action( 'woocommerce_after_shop_loop_item', 'custom_booking', 10 );
function custom_booking() {
global $product;
do_action( 'woocommerce_' . $product->get_type() . '_add_to_cart' );
}
}
and the booking form has been displayed under each product but it's not functional. If I use the second one, then the calendar will be unfunctional on a single-product too. Therefore how to achieve this?

Related

Laravel Nova show computed field in BelongsToMany sub table

Using Laravel Nova, I want to show a computed field in a BelongsToMany subview. In the intermediate model I have setup a virtual attribute field which computes a value using connected tables. However this field is not getting shown probably because Nova checks the withPivot fields (to which it cannot be added as it's not a real field). Is there an alternative method to get this to work?
The database model behind this is:
[GameVariation] has BelongsToMany using [GameVariationMatch] with [GameMatch]
[GameMatch] HasOne [Match]
In the GameVariation resource I've setup a BelongsToMany field which should display the computed field:
BelongsToMany::make( 'Game Matches', 'game_matches', GameMatch::class )
->fields( function () {
return [
Text::make( 'Match Data' )->displayUsing(function() {
return $this->match_data;
})
];
} ),
In the GameVariation model the tables are connected with BelongsToMany:
final public function game_matches(): BelongsToMany {
return $this->belongsToMany(
GameMatch::class
)
->using( GameVariationMatch::class );
}
In the pivot tabel model GameVariationMatch the computed field is setup like this:
final public function getMatchDataAttribute(): string {
return $this
->game_match
->match
->match_data;
}
As it turns out, on the GameVariation resource the index view of the BelongsToMany to GameMatch is served from the GameMatch resource. When only setting up ->fields() on the GameVariation BelongsToMany field, it won't show up. The recommended way is to set up ->fields() using a field class like this:
<?php
namespace App\Nova\Fields;
use App\Models\GameVariationMatch;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\Text;
class GameVariationMatchFields {
/**
* Get the pivot fields for the relationship.
*
* #param Request $request
*
* #return array
*/
final public function __invoke( Request $request ): array {
return [
Text::make( 'Match Data', function ( GameVariationMatch $GameVariationMatch ) {
return $GameVariationMatch->match_data;
} ),
];
}
}
In the above, the computed Text field receives the intermediate pivot model and can therefore access all computed attributes.
The field class is used in the GameVariation resource as:
BelongsToMany::make( 'Game Matches', 'game_matches', GameMatch::class )
->fields( new RoundMatchVariationFields ),
And in the GameMatch resource as:
BelongsToMany::make( 'Game Variations', 'game_variations', GameVariation::class )
->fields( new GameVariationMatchFields ),
As described in official Nova docs on pivot fields https://nova.laravel.com/docs/2.0/resources/relationships.html#belongstomany

Filter courses according to there category using angular material checkbox

I have two array called 1.Courses and 2.Categories each courses have different category i want to filter the courses by category using mat-checkbox.
example: javascript is a course name and scripting is category .
Here is the stackblitz link
and below is the screen shot of the approach:
It should work on multiple checkbox filtering Thank you in advance.
So, the simplest way to do this with your current approach, IMO, would be to create a new course array filteredCourses and iterate that in your template.
OnInit, set filteredCourses to courses so it renders them all on init.
ngOnInit() {
this.filteredCourses = this.courses;
}
Next, you need some way of maintaining a list of the selected categories. This would be much easier if you used Angulars built in forms, but, in the absence of that, may I suggest the following:
onSelect (click), add the clicked category to a list of selected categories (on click, if it's not there, add it, else, remove it)
onSelect(selectedCategory: any) {
this.selectCategory(selectedCategory);
}
selectCategory(selectedCategory: any) {
const index: number = this.selectedCategories.findIndex((cat: any) => {
return cat.id === selectedCategory.id
});
if ( index === -1) {
this.selectedCategories.push(selectedCategory);
} else {
this.selectedCategories.splice(index, 1);
}
}
The next step would then to be to filter your courses array to only those where the the categoryId is included in the list of selectedCategories and set your filteredCourses array with the result, allowing the template to update. So, your onSelect function becomes:
onSelect(selectedCategory: any) {
this.selectCategory(selectedCategory);
this.filteredCourses = this.courses.filter((course: any) => {
return this.selectedCategories.findIndex((cat: any) => {
return course.categoryId === cat.id;
}) !== -1;
});
}
Updated blitz with suggestion: https://stackblitz.com/edit/mat-checkbox-kq6xgd

Strategy for modifying a route parameter dynamically within the app

I'm developing a simple app that fetches the weekly lunch menu from a server. The query looks something like:
query {
lunchMenu(date: $date) {
id
name
etc.
}
}
I've defined a Route like so:
class LunchRoute extends Relay.Route {
static queries = {
lunchMenu: () => Relay.QL`
query { lunchMenu(date: $date) }
`,
};
static paramDefinitions = {
date: {required: false},
};
static routeName = 'LunchRoute';
}
I want the user to be able to select the date within the app via a datepicker and I've developed a LunchBrowser component which does that and renders the lunch menu. In index.js I have:
const lunchRoute = new LunchRoute({date: new Date()});
ReactDOM.render(
<Relay.RootContainer
Component={LunchBrowser}
route={lunchRoute}
/>,
document.getElementById('root')
);
The question is: how do I communicate a change in the date selection (from the LunchBrowser component) up one level so that I can modify the 'date' parameter in the Route and re-render?
I suppose I could have the LunchBrowser fire off some window event that is caught in index.js that does this, but this seems very wrong in that communication is flowing "up" from a lower-level component outside of React.
Or should I redefine my schema such that lunchMenu(date: $date) is pushed down one level? As in:
query {
lunch {
lunchMenu(date: $date) {
id
name
etc.
}
}
}
in which case lunch(date: $date) can be pushed down into LunchBrowserContainer's fragment and I no longer have to worry about dynamically modifying the Route's parameter.

ZF2 User group specific layouts

I have a project with 3 different Layouts. 1 Layout for the Login page, 1 Layout for the administrators and editors and a 3rd Layout for general users where also the administrators have access to (its a layout for 3 different pages where you can fill out surveys which is possible for the 3 mentioned user groups).
At the moment I am using EdpModuleLayouts for this purpose, which did fine until now. Because now I need to adapt the 3rd layout depending on which usergroup accesses it.
Do you have any idea how to get this done?
Thanks in advance.
you can listen you to the zf2 mvc events and change your layout based on your criterias.
this is an example how your application Module.php can look like.
public function onBootstrap(MvcEvent $e)
{
$application = $e->getApplication();
$serviceManager = $application->getServiceManager();
$eventManager = $application->getEventManager();
$sharedManager = $eventManager->getSharedManager();
// DISPATCH EVENT
$sharedManager->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function( MvcEvent $e) use ($serviceManager) {
// get your instance to locate current user group or something else
$auth = $serviceManager->get('Some/Auth/Service');
$userGroup = $auth->getUserGroupFromCurrentUser();
$controller = $e->getTarget();
if( $userGroup == 'someGroup' )
{
$controller->layout('layout/somelayoutname');
} else {
$controller->layout('layout/someotherlayoutname');
}
}, 50 );
}

symfony admin filter with join

I have a table, heading, that has an import_profile_id. import_profile has a bank_id.
On my headings list page in my admin, I'd like to add the ability to filter by bank_id. However, since heading doesn't have a bank_id - it needs to go through import_profile to get that - I can't just add a bank_id field and expect it to work.
Can anyone explain how to do this? The closest thing I've found is this post but I don't think it really addresses my issue.
This can be done by using virtual columns like the post you found. The virtual column is a way to add a new criteria to filter using the autogenerated filter provided by symfony. It works like this:
1 - Go to the generator.yml of the admin module and add the name of the virtual column that will create and add
<!-- apps/backend/modules/module_name/config/generator.yml -->
filter:
[virtual_column_name, and, other, filter, columns]
2 - In your lib/filter/{TableName}FormFilter.class.php (I think in your case must be HeadingFormFilter) you have to define that virtual column in the configure() method
public function configure()
{
//Type of widget (could be sfWidgetFormChoice with bank names)
$this->widgetSchema['virtual_column_name'] = new sfWidgetFormInputText(array(
'label' => 'Virtual Column Label'
));
//Type of validator for filter
$this->validatorSchema['virtual_column_name'] = new sfValidatorPass(array ('required' => false));
}
3 - Override the getFields() of that class to define it in the filter and set the filter function
public function getFields()
{
$fields = parent::getFields();
//the right 'virtual_column_name' is the method to filter
$fields['virtual_column_name'] = 'virtual_column_name';
return $fields;
}
4 - Finally you have to define the filter method. This method must be named after the add...ColumnQuery pattern, in our case must be addVirtualColumnNameColumnQuery(not a happy name choice :P), so
public function addVirtualColumnNameColumnQuery($query, $field, $value)
{
//add your filter query!
//for example in your case
$rootAlias = $query->getRootAlias();
$query->innerJoin($rootAlias . '.ImportProfile ip')
->andWhere('ip.BankId = ?', $value);
//remember to return the $query!
return $query;
}
Done! You can know filter by bank_id.

Resources